1 2<!DOCTYPE html> 3<html> 4<head> 5 <meta charset="utf-8" /> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover, maximum-scale=1.0" /> 7 <meta name="format-detection" content="telephone=no" /> 8 <meta property="og:locale" content="en_US" /> 9 <meta property="og:type" content="website" /> 10 <meta property="og:title" content="Rock Solid Forming, Inc. - Blue River, WI" /> 11 <meta property="og:url" content="https://rocksolidforminginc.com/" /> 12 <meta property="og:image" content="https://du9m0k402rjmo.cloudfront.net/images/P_16169/45092638-50b8-4ffa-81a2-8a1c376839cc.jpg" /> 13 <meta property="og:description" content="Rock Solid Forming, Inc. â Trusted Concrete Forming Solutions Since 1992. Located in Blue River, WI, Rock Solid Forming, Inc. has been a leading provider of concrete forms across Wisconsin, Iowa, and Minnesota for over 30 years. We specialize in sales, rentals, and renovation of high-quality concrete forms, serving contractors and construction professionals with dependable service and reliable products. As a family-owned business, we take pride in putting our customers first, working hard every day to meet your needs with efficiency, honesty, and attention to detail. With our own fleet of trucks and trailers, we ensure timely and professional delivery to your job site. Looking for a concrete forming partner you can count on? Contact Shane today at (608) 536-3700 â and let Rock Solid Forming be the foundation of your next project." /> 14 <meta name="description" content="Rock Solid Forming, Inc. â Trusted Concrete Forming Solutions Since 1992. Located in Blue River, WI, Rock Solid Forming, Inc. has been a leading provider of concrete forms across Wisconsin, Iowa, and Minnesota for over 30 years. We specialize in sales, rentals, and renovation of high-quality concrete forms, serving contractors and construction professionals with dependable service and reliable products. As a family-owned business, we take pride in putting our customers first, working hard every day to meet your needs with efficiency, honesty, and attention to detail. With our own fleet of trucks and trailers, we ensure timely and professional delivery to your job site. Looking for a concrete forming partner you can count on? Contact Shane today at (608) 536-3700 â and let Rock Solid Forming be the foundation of your next project." /> 15 <meta name="title" content="Rock Solid Forming, Inc. - Blue River WI" /> 16 <meta name="keywords" content="Concrete Forms,Construction Supply,Concrete Contractors,Building Supply" /> 17 <title>Rock Solid Forming, Inc. - Blue River, WI</title> 18
18<script src="/Scripts/Profile/googleAnalytics.js" type="text/javascript"></script>
vendor: 112 bytes, lines 18-21
18 19 <script type="text/javascript" src="https://www.google.com/recaptcha/api.js" async defer></script> 20 21
21<script src="https://code.jquery.com/jquery-3.5.0.min.js" integrity="sha256-xNzN2a4ltkB44Mc/Jz3pT4iU1cmeR0FkXs4pru/JxaQ=" crossorigin="anonymous"></script>
21 22 23
23<script>window.jQuery || document.write('
23<script src="/scripts/templates/jquery-3.5.0.min.js"><\/script>')</script>
23 24 25
25<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
25 26 27
27<script>if (typeof (Popper) === 'undefined') { document.write('
27<script src="/scripts/templates/popper.min.js"><\/script>') }</script>
27 28 29 <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css" integrity="sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt" crossorigin="anonymous"> 30 31 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous"> 32 33
33<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-mrcBkRTg0HmTfecoh1I+48Ptm7cAe18uN+3GsS5SXXGSwzpjIhJZkxEJ5jYaZmE" crossorigin="anonymous"></script>
33 34 35
35<script>if (typeof ($.fn.modal) === 'undefined') { document.write('
35<script src="/scripts/templates/bootstrap.min.js"><\/script>') }</script>
35 36 37
37<script src="/Scripts/Templates/googleMaps.js" type="text/javascript"></script>
37 38 <link href="/Content/Templates/custom-bootstrap.css" type="text/css" rel="stylesheet" /> 39
39<script src="/Scripts/bootbox.min.js" type="text/javascript"></script>
39 40
40<script src="/Scripts/Profile/jquery.barrating.1.1.4.js" type="text/javascript"></script>
40 41
41<script src="/Scripts/Templates/cleave.min.js" type="text/javascript"></script>
41 42
42<script src="/Scripts/Templates/cleave-phone.us.js" type="text/javascript"></script>
42 43
43<script src="/Scripts/Templates/slick.min.js" type="text/javascript"></script>
43 44 <link href="/Content/Templates/slick.css" type="text/css" rel="stylesheet" /> 45 <link href="/Content/Templates/slick-theme.css" type="text/css" rel="stylesheet" /> 46
46<script src="/Scripts/Templates/googleMapsMultiLocation.js" type="text/javascript"></script>
vendor: 66 bytes, lines 46-47
46 47 <script src="https://www.google.com/recaptcha/api.js?render=
476LcokacUAAAAAH_snk3O5bpdYwjpC5XAuXNLrJLH
vendor: 40 bytes, lines 47-48
47" type="text/javascript"></script> 48
48<script src="https://player.vimeo.com/api/player.js" type="text/javascript"></script>
48 49 50 <link href="/Content/Templates/chatbot.css" type="text/css" rel="stylesheet" /> 51 52 <meta name="viewport" content="width=device-width, user-scalable=yes,initial-scale=1, maximum-scale=2" /> 53 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> 54 55 56 <link href="/Content/Templates/platinum_fonts.css" type="text/css" rel="stylesheet" /> 57 58 <style> 59 :root { 60 --body_font: Roboto; 61 --header_font: Raleway; 62 --text_color: 255, 255, 255, 1; 63 --text_color_rgba: rgba(var(--text_color)); 64 --customer_color: #000000; 65 --slogan_font_size: 2.5rem; 66 --slogan_text_color: #A02223; 67 --faq_question_text_color: #000000; 68 --post_text_color: 0, 0, 0, 1; 69 --post_text_color_rgba: rgba(var(--post_text_color)); 70 --text-transform: capitalize; 71 --scroll-padding-top: 0px; /* Initial value */ 72 } 73 </style> 74 <link href="/Content/Templates/platinum_1.css" type="text/css" rel="stylesheet" /> 75 76 <link rel="canonical" href="https://rocksolidforminginc.com/"> 77 78
79<script type="application/ld+json"> 80{ 81 "@context": "https://schema.org", 82 "@graph": [ 83 { 84 "@type": "LocalBusiness", 85 "@id": "https://rocksolidforminginc.com/#localbusiness", 86 "name": "Rock Solid Forming, Inc.", 87 "url": "https://rocksolidforminginc.com/", 88 "description": "Rock Solid Forming, Inc. â Trusted Concrete Forming Solutions Since 1992. Located in Blue River, WI, Rock Solid Forming, Inc. has been a leading provider of concrete forms across Wisconsin, Iowa, and Minnesota for over 30 years. We specialize in sales, rentals, and renovation of high-quality concrete forms, serving contractors and construction professionals with dependable service and reliable products. As a family-owned business, we take pride in putting our customers first, working hard every day to meet your needs with efficiency, honesty, and attention to detail. With our own fleet of trucks and trailers, we ensure timely and professional delivery to your job site. Looking for a concrete forming partner you can count on? Contact Shane today at (608) 536-3700 â and let Rock Solid Forming be the foundation of your next project.", 89 "image": "https://du9m0k402rjmo.cloudfront.net/images/P_16169/45092638-50b8-4ffa-81a2-8a1c376839cc.jpg", 90 "logo": "https://du9m0k402rjmo.cloudfront.net/images/P_16169/45092638-50b8-4ffa-81a2-8a1c376839cc.jpg", 91 "address": { 92 "@type": "PostalAddress", 93 "streetAddress": "30397 Cty. Hwy. X", 94 "addressLocality": "Blue River", 95 "addressRegion": "WI", 96 "postalCode": "53518", 97 "addressCountry": "US" 98 }, 99 "telephone": "(608) 536-3700", 100 "email": "[email protected]", 101 "geo": { 102 "@type": "GeoCoordinates", 103 "latitude": 43.19665, 104 "longitude": -90.56485 105 }, 106 "openingHoursSpecification": [ 107 { 108 "@type": "OpeningHoursSpecification", 109 "dayOfWeek": "Monday", 110 "opens": "06:00", 111 "closes": "21:00" 112 }, 113 { 114 "@type": "OpeningHoursSpecification", 115 "dayOfWeek": "Tuesday", 116 "opens": "06:00", 117 "closes": "21:00" 118 }, 119 { 120 "@type": "OpeningHoursSpecification", 121 "dayOfWeek": "Wednesday", 122 "opens": "06:00", 123 "closes": "21:00" 124 }, 125 { 126 "@type": "OpeningHoursSpecification", 127 "dayOfWeek": "Thursday", 128 "opens": "06:00", 129 "closes": "21:00" 130 }, 131 { 132 "@type": "OpeningHoursSpecification", 133 "dayOfWeek": "Friday", 134 "opens": "06:00", 135 "closes": "21:00" 136 }, 137 { 138 "@type": "OpeningHoursSpecification", 139 "dayOfWeek": "Saturday", 140 "opens": "06:00", 141 "closes": "21:00" 142 }, 143 { 144 "@type": "OpeningHoursSpecification", 145 "dayOfWeek": "Sunday", 146 "opens": "06:00", 147 "closes": "21:00" 148 } 149 ], 150 "paymentAccepted": "Personal Checks", 151 "sameAs": [ 152 "https://www.google.com/maps/place/Rock+Solid+Forming/@43.2589622,-90.591166,17z/data=!3m1!4b1!4m5!3m4!1s0x87fdaca56853265f:0x9697f03043c2e051!8m2!3d43.2589622!4d-90.591166", 153 "https://www.yelp.com/biz/rock-solid-forming-inc-blue-river", 154 "https://facebook.com/Rock-Solid-Forming-Inc-277697514079878/", 155 "https://www.linkedin.com/in/shane-dilley-b72a162b/" 156 ] 157 }, 158 { 159 "@type": "WebSite", 160 "@id": "https://rocksolidforminginc.com/#website", 161 "url": "https://rocksolidforminginc.com/", 162 "name": "Rock Solid Forming, Inc.", 163 "description": "Rock Solid Forming, Inc. â Trusted Concrete Forming Solutions Since 1992. Located in Blue River, WI, Rock Solid Forming, Inc. has been a leading provider of concrete forms across Wisconsin, Iowa, and Minnesota for over 30 years. We specialize in sales, rentals, and renovation of high-quality concrete forms, serving contractors and construction professionals with dependable service and reliable products. As a family-owned business, we take pride in putting our customers first, working hard every day to meet your needs with efficie
163ncy, honesty, and attention to detail. With our own fleet of trucks and trailers, we ensure timely and professional delivery to your job site. Looking for a concrete forming partner you can count on? Contact Shane today at (608) 536-3700 â and let Rock Solid Forming be the foundation of your next project.", 164 "potentialAction": { 165 "@type": "SearchAction", 166 "target": "https://rocksolidforminginc.com/?s={search_term_string}", 167 "query-input": "required name=search_term_string" 168 } 169 }, 170 { 171 "@type": "WebPage", 172 "@id": "https://rocksolidforminginc.com/#webpage", 173 "url": "https://rocksolidforminginc.com/", 174 "name": "Rock Solid Forming, Inc. - Blue River, WI", 175 "description": "Rock Solid Forming, Inc. â Trusted Concrete Forming Solutions Since 1992. Located in Blue River, WI, Rock Solid Forming, Inc. has been a leading provider of concrete forms across Wisconsin, Iowa, and Minnesota for over 30 years. We specialize in sales, rentals, and renovation of high-quality concrete forms, serving contractors and construction professionals with dependable service and reliable products. As a family-owned business, we take pride in putting our customers first, working hard every day to meet your needs with efficiency, honesty, and attention to detail. With our own fleet of trucks and trailers, we ensure timely and professional delivery to your job site. Looking for a concrete forming partner you can count on? Contact Shane today at (608) 536-3700 â and let Rock Solid Forming be the foundation of your next project.", 176 "isPartOf": { 177 "@id": "https://rocksolidforminginc.com/#website" 178 }, 179 "about": { 180 "@id": "https://rocksolidforminginc.com/#localbusiness" 181 }, 182 "primaryImageOfPage": { 183 "@type": "ImageObject", 184 "url": "https://du9m0k402rjmo.cloudfront.net/images/P_16169/45092638-50b8-4ffa-81a2-8a1c376839cc.jpg" 185 }, 186 "breadcrumb": { 187 "@type": "BreadcrumbList", 188 "itemListElement": [ 189 { 190 "@type": "ListItem", 191 "position": 1, 192 "name": "Home", 193 "item": "https://rocksolidforminginc.com/" 194 } 195 ] 196 } 197 }, 198 { 199 "@type": "Service", 200 "@id": "https://rocksolidforminginc.com/#service", 201 "name": "Our Services", 202 "provider": { 203 "@id": "https://rocksolidforminginc.com/#localbusiness" 204 }, 205 "serviceType": [ 206 "Sales", 207 "Rentals", 208 "Renovations", 209 "Steel-ply Forms", 210 "Ties", 211 "Hardware", 212 "Form Release" 213 ], 214 "description": "Professional services offered by Rock Solid Forming, Inc." 215 }, 216 { 217 "@type": "Service", 218 "@id": "https://rocksolidforminginc.com/#service1", 219 "name": "Concrete Form Sales", 220 "provider": { 221 "@id": "https://rocksolidforminginc.com/#localbusiness" 222 }, 223 "serviceType": "Concrete Form Sales", 224 "description": "When quality and durability matter most, our concrete form sales provide the reliable solutions contractors depend on. Rock Solid Forming, Inc. offers a wide selection of high-performance products, including steel-ply forms, ties, hardware, and form release supplies to meet the demands of any job site. As your local Symons/Dayton Superior dealer, we supply trusted materials designed for strength, efficiency, and long-term use. Our knowledgeable team is here to help you select the right products for your project, ensuring you have everything needed to build with confidence and precision from the ground up." 225 }, 226 { 227 "@type": "Service", 228 "@id": "https://rocksolidforminginc.com/#service2", 229 "name": "Concrete Form Rentals", 230 "provider": { 231 "@id": "https://rocksolidforminginc.com/#localbusiness" 232 }, 233 "serviceType": "Concrete Form Rentals", 234 "description": "For projects that require flexibility and cost-effective options, our concrete form rental services are the perfect solution. We maintain a large inventory of dependable, well-maintained forms and accessories ready for immediate use. Whether youâre tackling a short-term project or need additional equipment to meet tight deadlines, our rental options help you stay efficie
234nt without the commitment of purchasing. With prompt delivery from our in-house fleet and responsive customer support, Rock Solid Forming, Inc. makes it easy to get the equipment you need, when and where you need it." 235 }, 236 { 237 "@type": "Service", 238 "@id": "https://rocksolidforminginc.com/#service3", 239 "name": "Concrete Form Renovations", 240 "provider": { 241 "@id": "https://rocksolidforminginc.com/#localbusiness" 242 }, 243 "serviceType": "Concrete Form Renovations", 244 "description": "Extend the life of your equipment with our professional concrete form renovation services designed to restore performance and reliability. Our experienced team carefully inspects, repairs, and refurbishes worn or damaged forms, helping you avoid the cost of full replacement while maintaining job site efficiency. We focus on bringing your forms back to peak condition so they continue to deliver consistent, high-quality results. At Rock Solid Forming, Inc., we combine craftsmanship and attention to detail to ensure your equipment performs like new, keeping your projects running smoothly and your investment working harder for you." 245 }, 246 { 247 "@type": "FAQPage", 248 "@id": "https://rocksolidforminginc.com/#faqpage", 249 "url": "https://rocksolidforminginc.com/#faq", 250 "name": "Frequently Asked Questions", 251 "description": "Frequently asked questions about Rock Solid Forming, Inc.", 252 "mainEntity": [ 253 { 254 "@type": "Question", 255 "name": "What does Rock Solid Forming, Inc. specialize in?", 256 "acceptedAnswer": { 257 "@type": "Answer", 258 "text": "Rock Solid Forming, Inc. specializes in concrete forming solutions for contractors and construction professionals. Our services include concrete form sales, rentals, and renovations, providing customers with dependable equipment and support for projects of all sizes. With more than 30 years of experience, we help ensure construction projects have the quality forming systems needed for successful results." 259 } 260 }, 261 { 262 "@type": "Question", 263 "name": "What services do you provide?", 264 "acceptedAnswer": { 265 "@type": "Answer", 266 "text": "We offer: Sales of forming products (steel-ply forms, ties, hardware) Rentals of forming equipment and materials Renovations / repair / refurbishment of formwork systems" 267 } 268 }, 269 { 270 "@type": "Question", 271 "name": "How can I get a quote or request a rental?", 272 "acceptedAnswer": { 273 "@type": "Answer", 274 "text": "You can contact us via phone at (608) 536-3700 or via the âContactâ page/form on our website. We will discuss your project details (size, duration, materials) and provide you with a customized quote." 275 } 276 }, 277 { 278 "@type": "Question", 279 "name": "Can I rent concrete forms instead of purchasing them?", 280 "acceptedAnswer": { 281 "@type": "Answer", 282 "text": "Absolutely. Our concrete form rental program provides a cost-effective solution for contractors who need equipment for specific projects without the expense of ownership. We offer reliable, well-maintained forms that help maximize efficiency and productivity on the job site." 283 } 284 }, 285 { 286 "@type": "Question", 287 "name": "What are concrete form renovation services?", 288 "acceptedAnswer": { 289 "@type": "Answer", 290 "text": "Concrete form renovation involves restoring and refurbishing existing concrete forming equipment to improve performance, extend service life, and maintain reliability. Our renovation services can help contractors get more value from their equipment while avoiding the cost of complete replacement." 291 } 292 }, 293 { 294 "@type": "Question", 295 "name": "Why choose Rock Solid Forming, Inc. for concrete form solutions?", 296 "acceptedAnswer": { 297 "@type": "Answer", 298 "text": "Customers choose Rock Solid Forming, Inc. because of our extensive industry experience, commitment to quality, and customer-first approach. As a family-owned company, we focus on honest service, dependable products, and practical solutions that help contractors complete projects successfully and on schedule." 299 } 300 } 301 ] 302 } 303 ] 304} 305</script>
305</head> 306<body> 307 <div id="bootstrapCssTest" class="d-none"></div> 308
308<script> 309 $(function () { 310 311 if ($('#bootstrapCssTest').is(':visible')) { 312 $("head").prepend('<link rel="stylesheet" href="/content/templates/bootstrap.min.css">'); 313 } 314 }); 315 </script>
315 316 317 318 319 320<input type="hidden" id="areaMarkers" /> 321<input type="hidden" id="selectedServiceId" value="" /> 322 323<!-- NAVBAR --> 324<div class="container-fullwidth fixed-top"> 325 326 327 328 329 330<header id="headernav" class="content-header custom-background-color d-flex flex-wrap align-items-center justify-content-between py-0 px-2"> 331 332 333 <div class="nav-phone-social pb-1 d-flex flex-wrap justify-content-center"> 334 <div class="my-1 header-address d-flex flex-wrap justify-content-center"> 335 <div class="nav-phone text-center nav-header-item-no-spacing mx-1"><a id="PhoneNav" class="text-font-color phone-nav-mobile" href="tel:6085363700"><i class="fas fa-phone fa-inverse fa-xs fa-rotate-90 mr-1"></i>(608) 536-3700</a></div> 336 <div class="nav-phone text-center nav-header-item-no-spacing mx-1 px-1"><a id="eMailNav" class="text-font-color phone-nav-mobile text-lowercase" href="mailto:[email protected]" target="_blank"><i class="fas fa-envelope fa-inverse fa-sm mr-1"></i>[email protected]</a></div> 337 </div> 338 </div> 339 340 341 <div class="nav-phone-social pb-1 d-flex flex-wrap justify-content-center"> 342 <div class="nav-social mx-1 px-1"> 343 <div class="mx-auto text-center d-flex flex-wrap justify-content-center"> 344 <div class="mt-1 mb-1 ml-2 mr-2"> 345 <a class="text-font-color social-link-footer" href="https://www.google.com/maps/place/Rock+Solid+Forming/@43.2589622,-90.591166,17z/data=!3m1!4b1!4m5!3m4!1s0x87fdaca56853265f:0x9697f03043c2e051!8m2!3d43.2589622!4d-90.591166" target="_blank" id="google-social-footer" title="Google"> 346 <i class="fab fa-google fa-lg"></i> 347 </a> 348 </div> 349 <div class="mt-1 mb-1 ml-2 mr-2"> 350 <a class="text-font-color social-link-footer" href="https://facebook.com/Rock-Solid-Forming-Inc-277697514079878/" target="_blank" id="facebook-social-footer" title="Facebook"> 351 <i class="fab fa-facebook-f fa-lg"></i> 352 </a> 353 </div> 354 <div class="mt-1 mb-1 ml-2 mr-2"> 355 <a class="text-font-color social-link-footer" href="https://www.yelp.com/biz/rock-solid-forming-inc-blue-river" target="_blank" id="yelp-social-footer" title="Yelp"> 356 <i class="fab fa-yelp fa-lg"></i> 357 </a> 358 </div> 359 <div class="mt-1 mb-1 ml-2 mr-2"> 360 <a class="text-font-color social-link-footer" href="https://www.linkedin.com/in/shane-dilley-b72a162b/" target="_blank" id="linkedin-social-footer" title="LinkedIn"> 361 <i class="fab fa-linkedin-in fa-lg"></i> 362 </a> 363 </div> 364 </div> 365 </div> 366 </div> 367</header> 368 369 370<nav class="bg-default-color bg-light navbar navbar-light navbar-expand-xl pt-1"> 371 372 <div class="nav-image bg-default-color bg-light flex-column text-center nav-image-desktop"> 373 <a href="#home-section" class="navbar-brand ml-3" id="HomeNav" onclick="DisplayPage('#homePage')"> 374 <img src="https://du9m0k402rjmo.cloudfront.net/images/P_16169/45092638-50b8-4ffa-81a2-8a1c376839cc.jpg" alt="navbar logo" class="d-line-block align-top nav-logo-medium" /> 375 </a> 376 </div> 377 378 <button class="navbar-toggler nav-menu collapsed" type="button" data-toggle="collapse" data-target="#navbarToggler" aria-controls="navbarToggler" aria-expanded="false" aria-label="Toggle navigation"> 379 <span class="navbar-toggler-icon"></span> 380 <span class="my-1 mx-2 close">X</span> 381 </button> 382 383 <div class="collapse navbar-collapse flex-column mx-auto" id="navbarToggler"> 384 385 <div class="d-none d-xl-block nav-section"> 386 <div class="navbar-nav d-flex flex-wrap align-items-center"> 387 388
389<div class="col nav-item text-center nav-header-item mx-auto text-nowrap"> <a href="#" id="HomePageNavigationLink" class="nav-link header-font p-3" onclick="DisplayPage('#homePage')">Home</a> 390</div><div class="col nav-item text-center nav-header-item mx-auto text-nowrap"> <a href="#" id="AboutPageNavigationLink" class="nav-link header-font p-3" onclick="DisplayPage('#aboutPage')">About</a> 391</div><div class="col nav-item text-center nav-header-item mx-auto text-nowrap"> <a href="#" id="PhotosNavigationLink" class="nav-link header-font p-3" onclick="DisplayPage('#imageryPage')">Gallery</a> 392</div><div class="col nav-item text-center nav-header-item mx-auto text-nowrap"> <a href="#" id="ServicesPageNavigationLink" class="nav-link header-font p-3" onclick="DisplayPage('#servicesPage')">Services</a> 393</div><div class="col nav-item text-center nav-header-item mx-auto text-nowrap"> <a href="#" id="testimonialsPageNavigationLink" class="nav-link header-font p-3" onclick="DisplayPage('#testimonialsPage')">Testimonials</a> 394</div><div class="col nav-item text-center nav-header-item mx-auto text-nowrap"> <a href="#" id="ContactPageNavigationLink" class="nav-link header-font p-3" onclick="DisplayPage('#contactPage')">Contact</a> 395</div><div class="col nav-item text-center nav-header-item mx-auto text-nowrap"> <a href="#" id="PostsPageNavigationLink" class="nav-link header-font p-3" onclick="DisplayPage('#postsPage')">News</a> 396</div><div class="col nav-item text-center nav-header-item mx-auto text-nowrap"> <a href="#" id="FaqPageNavigationLink" class="nav-link header-font p-3" onclick="DisplayPage('#faqPage')">FAQ</a> 397</div> </div> 398 </div> 399 400 <div class="d-xs-block d-xl-none"> 401 <ul class="navbar-nav mx-auto mr-auto mt-2 mt-xl-0 mb-2"> 402 403 404<li class="nav-header-item-no-spacing mr-1 ml-1 text-nowrap"> <a href="#" id="HomePageNavigationLink" class="nav-link header-font pl-3" onclick="DisplayPage('#homePage')">Home</a> 405</li><li class="nav-header-item-no-spacing mr-1 ml-1 text-nowrap"> <a href="#" id="AboutPageNavigationLink" class="nav-link header-font pl-3" onclick="DisplayPage('#aboutPage')">About</a> 406</li><li class="nav-header-item-no-spacing mr-1 ml-1 text-nowrap"> <a href="#" id="PhotosNavigationLink" class="nav-link header-font pl-3" onclick="DisplayPage('#imageryPage')">Gallery</a> 407</li><li class="nav-header-item-no-spacing mr-1 ml-1 text-nowrap"> <a href="#" id="ServicesPageNavigationLink" class="nav-link header-font pl-3" onclick="DisplayPage('#servicesPage')">Services</a> 408</li><li class="nav-header-item-no-spacing mr-1 ml-1 text-nowrap"> <a href="#" id="testimonialsPageNavigationLink" class="nav-link header-font pl-3" onclick="DisplayPage('#testimonialsPage')">Testimonials</a> 409</li><li class="nav-header-item-no-spacing mr-1 ml-1 text-nowrap"> <a href="#" id="ContactPageNavigationLink" class="nav-link header-font pl-3" onclick="DisplayPage('#contactPage')">Contact</a> 410</li><li class="nav-header-item-no-spacing mr-1 ml-1 text-nowrap"> <a href="#" id="PostsPageNavigationLink" class="nav-link header-font pl-3" onclick="DisplayPage('#postsPage')">News</a> 411</li><li class="nav-header-item-no-spacing mr-1 ml-1 text-nowrap"> <a href="#" id="FaqPageNavigationLink" class="nav-link header-font pl-3" onclick="DisplayPage('#faqPage')">FAQ</a> 412</li> </ul> 413 </div> 414 </div> 415</nav> 416</div> 417 418<div id="pages" class="pages-left-medium"> 419 <!--Home Page Information--> 420 <div class="content collapse show bg-alt-color" id="homePage"> 421 422<div class="d-flex flex-wrap"> 423 424 <div class="w-100 mx-auto home-banner"> 425 <div id="homepagebanner" class="parallax w-100 aspect-ratio-16x9"> 426 <div class="img-fluid banner-image"><object data="https://fast.wistia.com/embed/medias/ynagcpuavr?fitStrategy=cover&endVideoBehavior=loop&controlsVisibleOnLoad=false&autoPlay=true&volumeControl=false&muted=true&playbar=false&smallPlayButton=false&settingsControl=false&control-bar-right=false" style="width: 100%; height: 100%;" onload="jQuery('.w-css-reset').hide(); jQuery('.w-bottom-bar').hide(); jQuery('.w-ui-container').remove(); "></object></div> 427 428 <div class="home-content"> 429 <div class="d-flex flex-wrap justify-content-center align-items-center px-2"></div> 430 </div> 431 </div> 432 </div> 433 434 <div class="w-100 w-lg-80 w-sm-90 w-md-90 mx-auto p-2"> 435 <div class="d-flex flex-row flex-wrap justify-content-center text-left py-2"> 436 <div class="col-md mx-auto text-center pt-2"> 437 <img alt="Image" src="https://du9m0k402rjmo.
437cloudfront.net/images/P_16169/2bf6d30d-e281-4d0b-b1ce-7d3c9f214137.png" class="img-fluid mx-auto text-center" /> 438 </div> 439 <div class="col-md pt-2"> 440 <div class="mx-auto mb-2 section-information"> 441 <div class="strike"> 442 <span class="text-center header-item">About Us</span> 443 </div> 444 </div> 445 <div class="mx-auto mb-2 description-speakable" style="white-space: pre-line">Rock Solid Forming, Inc. â Trusted Concrete Forming Solutions Since 1992. Located in Blue River, WI, Rock Solid Forming, Inc. has been a leading provider of concrete forms across Wisconsin, Iowa, and Minnesota for over 30 years. We specialize in sales, rentals, and renovation of high-quality concrete forms, serving contractors and construction professionals with dependable service and reliable products. As a family-owned business, we take pride in putting our customers first, working hard every day to meet your needs with efficiency, honesty, and attention to detail. With our own fleet of trucks and trailers, we ensure timely and professional delivery to your job site. Looking for a concrete forming partner you can count on? Contact Shane today at (608) 536-3700 â and let Rock Solid Forming be the foundation of your next project.</div> 446 </div> 447 </div> 448 </div> 449 450 <div class="w-100 w-lg-80 w-sm-90 w-md-90 mx-auto p-2" id="photos-content"> 451 <div class="mx-auto mb-2 section-information"> 452 <div class="strike"> 453 <span class="text-center header-item">Highlights</span> 454 </div> 455 </div> 456 <div class="d-flex flex-row flex-wrap justify-content-center py-2"> 457 <div class="w-100 w-lg-30 w-md-30 w-sm-30 mx-auto"> 458 <div class="d-flex flex-wrap"> 459 <div class="col p-2"> 460<a href="#" id="photos-image-element1" class="thumbnail mx-auto d-block"> <img src="https://du9m0k402rjmo.cloudfront.net/images/P_16169/52d6a9eb-b2be-400b-ab48-d384636e3f5f.png" alt="" class="img-fluid d-block rounded mx-auto photo-image" /> 461 <div class="text-center pt-2" style="color:black">Concrete Form Sales</div> 462 </a> </div> 463 </div> 464 </div> 465 <div class="w-100 w-lg-30 w-md-30 w-sm-30 mx-auto"> 466 <div class="d-flex flex-wrap"> 467 <div class="col p-2"> 468<a href="#" id="photos-image-element2" class="thumbnail mx-auto d-block"> <img src="https://du9m0k402rjmo.cloudfront.net/images/P_16169/2d1ecb8e-d6d1-4cbf-8d69-74c8ae2030cf.png" alt="" class="img-fluid d-block rounded mx-auto photo-image" /> 469 <div class="text-center pt-2" style="color:black">Concrete Form Rentals</div> 470 </a> </div> 471 </div> 472 </div> 473 <div class="w-100 w-lg-30 w-md-30 w-sm-30 mx-auto"> 474 <div class="d-flex flex-wrap"> 475 <div class="col p-2"> 476<a href="#" id="photos-image-element3" class="thumbnail mx-auto d-block"> <img src="https://du9m0k402rjmo.cloudfront.net/images/P_16169/0dfc4498-76a4-403d-8310-ed8c1415fd4c.png" alt="" class="img-fluid d-block rounded mx-auto photo-image" /> 477 <div class="text-center pt-2" style="color:black">Concrete Form Renovations</div> 478 </a> </div> 479 </div> 480 </div> 481 </div> 482 </div> 483 484</div> 485 486 </div> 487 488 <!--About Page Information--> 489 <div class="content collapse bg-alt-color" id="aboutPage"></div> 490 491 <!--Services Page Information--> 492 <div class="content collapse bg-alt-color" id="servicesPage"></div> 493 494 <!--Testimonial Page Information--> 495 <div class="content collapse bg-alt-color" id="testimonialsPage"></div> 496
497 <!-- REVIEW MODAL --> 498 <div id="reviewFormParentContainer"></div> 499 500 <!--Contact Page Information--> 501 <div class="content collapse bg-alt-color" id="contactPage"></div> 502 503 <!--Imagery Page Information--> 504 <div class="content collapse bg-alt-color" id="imageryPage"></div> 505 506 <!--Posts Page Information--> 507 <div class="content collapse bg-alt-color" id="postsPage"> 508 509<div class="d-flex flex-wrap"> 510 511 512 <div class="w-100 mx-auto banner"> 513 <div class="parallax w-100"> 514 <div class="img-fluid banner-image " style="background-image: url(https://du9m0k402rjmo.cloudfront.net/images/P_16169/328a43dd-8694-4ede-8110-b2337e043719.png)"></div> 515 </div> 516 </div> 517 <div class="w-100 w-lg-80 w-sm-90 w-md-90 mx-auto p-2"> 518 <div class="d-flex flex-row flex-wrap justify-content-center text-center"> 519 <div class="posts-partial-content w-100"> 520 <div class="mx-auto mb-2 section-information pt-3"> 521 <div class="strike"> 522 <span class="text-center header-item">News</span> 523 </div> 524 </div> 525 526 <div class="postsInfo" id="testimonialPostsInfo"></div> 527 <div class="text-center m-3"> 528 <button type="button" id="prevPosts" class="btn btn-light btn-md" disabled><i class="fas fa-angle-left"></i> Previous</button> 529 <button type="button" id="nextPosts" class="btn btn-light btn-md">Next <i class="fas fa-angle-right"></i></button> 530 </div> 531 </div> 532 </div> 533 </div> 534</div> 535 </div> 536 537 <!--Locations Page Information--> 538 <div class="content collapse bg-alt-color" id="locationsPage"></div> 539 540 <!--FAQ Page Information--> 541 <div class="content collapse bg-alt-color" id="faqPage"></div> 542 543 <!--Additional Content Page Information--> 544 <div class="content collapse bg-alt-color" id="contentPage"></div> 545 546</div> 547 548<!--FOOTER Information--> 549<footer id="footer-container" class="footer custom-background-color"> 550 551 552<div class="container-fluid pt-1"> 553 <div class="row pl-3 pr-3"> 554 <div id="footer-address" class="col-sm-3 col-md-4 col-lg-3 mx-auto d-flex flex-wrap footer-content"> 555 <div class="justify-content-left text-font-color"> 556 <div class="footer-header mb-2 mt-2 text-left text-nowrap">Contact Information</div> 557 <div class="content-large">Rock Solid Forming, Inc.</div> 558 <div class="mt-1 content-medium"> 55930397 Cty. Hwy. X </div> 560 <div class="content-medium">Blue River, WI 53518</div> 561 <div class="mt-2 mb-2"> 562 <div class="mx-auto content-medium"> 563 <strong>Phone:</strong> <a id="footer_phoneLink" href="tel:6085363700" class="text-font-color footer-link">(608) 536-3700</a> 564 </div> 565 <div class="mx-auto content-medium"> 566 <strong>Email:</strong> <a id="footer_ContactEmail" title="[email protected]" class="text-font-color break-word-hyphen" href="mailto:[email protected]">shane<wbr>.rocksolid<wbr>@gmail<wbr>.com</a> 567 </div> 568 </div> 569 </div> 570 </div> 571 <div id="footer-hours" class='col-sm-3 col-md-4 col-lg-3 mx-auto footer-content d-flex flex-wrap justify-content-center'> 572 <div class="justify-content-left text-font-color"> 573 <div class="footer-hours-detail business-info-footer"> 574 <div class="row"> 575 <div class="col-12 footer-header mt-2 mb-2 text-nowrap">Our Hours</div> 576 </div> 577 <div class="row"> 578 <div class="col-12 content-medium pt-1 pb-1">Holiday Hours May Vary. Please Call For Details.</div> 579 </div> 580 <div class="row content-medium pt-1 pb-1"> 581 <div class="col-6"> 582 <div class="mr-3 font-weight-bold"> 583 Monday 584 </div> 585 </div> 586 <div class="col-6"> 587 <div class="contact-hours ml-1 text-nowrap"> 588 6:00 AM - 9:00 PM 589 </div> 590 </div> 591 </div> 592 593 <div class="row content-medium pt-1 pb-1"> 594 <div class="col-6"> 595 <div class="mr-3 font-weight-bold"> 596 Tuesday 597 </div> 598 </div> 599 <div class="col-6"> 600 <div class="contact-hours ml-1 text-nowrap"> 601 6:00 AM - 9:00 PM 602 </div> 603 </div> 604 </div> 605 606 <div class="row content-medium pt-1 pb-1"> 607 <div class="col-6"> 608 <div class="mr-3 font-weight-bold"> 609 Wednesday 610 </div> 611 </div> 612 <div class="col-6"> 613 <div class="contact-hours ml-1 text-nowrap"> 614 6:00 AM - 9:00 PM 615 </div> 616 </div> 617 </div> 618 619 <div class="row content-medium pt-1 pb-1"> 620 <div class="col-6"> 621 <div class="mr-3 font-weight-bold"> 622 Thursday 623 </div> 624 </div> 625 <div class="col-6"> 626 <div class="contact-hours ml-1 text-nowrap"> 627 6:00 AM - 9:00 PM 628 </div> 629 </div> 630 </div> 631 632 <div class="row content-medium pt-1 pb-1"> 633 <div class="col-6"> 634 <div class="mr-3 font-weight-bold"> 635 Friday 636 </div> 637 </div> 638 <div class="col-6"> 639 <div class="contact-hours ml-1 text-nowrap"> 640 6:00 AM - 9:00 PM 641 </div> 642 </div> 643 </div> 644 645 <div class="row content-medium pt-1 pb-1"> 646 <div class="col-6"> 647 <div class="mr-3 font-weight-bold"> 648 Saturday 649 </div> 650 </div> 651 <div class="col-6"> 652 <div class="contact-hours ml-1 text-nowrap"> 653 6:00 AM - 9:00 PM 654 </div> 655 </div> 656 </div> 657 658 <div class="row content-medium pt-1 pb-1"> 659 <div class="col-6"> 660 <div class="mr-3 font-weight-bold"> 661 Sunday 662 </div> 663 </div> 664 <div class="col-6"> 665 <div class="contact-hours ml-1 text-nowrap"> 666 6:00 AM - 9:00 PM 667 </div> 668 </div> 669 </div> 670 </div> 671 </div> 672 </div> 673 <div id="footer-certificationandassociation" class="col-sm-3 col-md-4 col-lg-3 mx-auto footer-content d-flex flex-wrap"> 674 <div class="justify-content-left text-font-color">
675 <div id="associationsHeader" class="footer-header text-left mb-2 mt-2 text-nowrap">Associations</div> 676 <div id="associations">Better Business Bureau A+ Rating</div> 677 </div> 678 </div> 679 <div id="footer-sitenav" class='col-sm-3 col-md-4 col-lg-3 mx-auto footer-content d-flex flex-wrap justify-content-center'> 680 <div class="justify-content-left text-font-color"> 681 <div class="footer-header text-left mb-2 mt-2 text-nowrap">Site Navigation</div> 682 683 684<div class="text-left content-medium mx-auto text-nowrap pb-1"> <a href="#" id="HomePageNavigationLink" class="text-font-color footer-link" onclick="DisplayPage('#homePage')">Home</a> 685</div><div class="text-left content-medium mx-auto text-nowrap pb-1"> <a href="#" id="AboutPageNavigationLink" class="text-font-color footer-link" onclick="DisplayPage('#aboutPage')">About</a> 686</div><div class="text-left content-medium mx-auto text-nowrap pb-1"> <a href="#" id="PhotosNavigationLink" class="text-font-color footer-link" onclick="DisplayPage('#imageryPage')">Gallery</a> 687</div><div class="text-left content-medium mx-auto text-nowrap pb-1"> <a href="#" id="ServicesPageNavigationLink" class="text-font-color footer-link" onclick="DisplayPage('#servicesPage')">Services</a> 688</div><div class="text-left content-medium mx-auto text-nowrap pb-1"> <a href="#" id="testimonialsPageNavigationLink" class="text-font-color footer-link" onclick="DisplayPage('#testimonialsPage')">Testimonials</a> 689</div><div class="text-left content-medium mx-auto text-nowrap pb-1"> <a href="#" id="ContactPageNavigationLink" class="text-font-color footer-link" onclick="DisplayPage('#contactPage')">Contact</a> 690</div><div class="text-left content-medium mx-auto text-nowrap pb-1"> <a href="#" id="PostsPageNavigationLink" class="text-font-color footer-link" onclick="DisplayPage('#postsPage')">News</a> 691</div><div class="text-left content-medium mx-auto text-nowrap pb-1"> <a href="#" id="FaqPageNavigationLink" class="text-font-color footer-link" onclick="DisplayPage('#faqPage')">FAQ</a> 692</div> <div class="text-left content-medium mx-auto text-nowrap"> 693 <a href="/Accessibility?domain=rocksolidforminginc" target="_blank" class="text-font-color footer-link"> 694 Text-Only Site Version 695 </a> 696 </div> 697 </div> 698 </div> 699 </div> 700</div> 701 702<div class="d-flex flex-wrap text-center justify-content-center pb-3 pl-3 pr-3"> 703 <div class="d-flex text-center text-nowrap tos pt-1 w-100 justify-content-center"> 704 <a class="text-font-color" href="/Home/TermsOfUse" id="tosLink" target="_blank">Terms of Use</a><div id="footerdash"> | </div><a class="text-font-color" href="/Home/PrivacyPolicy" id="ppLink" target="_blank">Privacy Policy</a> 705 </div> 706 <div class="d-flex text-center pt-2 w-100 justify-content-center" style="font-size: 0.75rem; color: #999;"> 707 © 2026 American Marketing & Publishing, L.L.C. All rights reserved. Names, logos, and other marks displayed are the property of their respective owners. 708 </div> 709 </div> 710</footer> 711 712 <div class="accessibility-img py-2 px-2"> 713 <div class="d-flex flex-wrap justify-content-center align-items-center text-center"> 714 <a href="/Accessibility?domain=rocksolidforminginc" target="_blank" class="dark"> 715 <img src="/Content/Templates/images/accessibility_icon.png" height="60" width="42" /> 716 </a> 717 </div> 718 </div> 719 720 721 722 723<div class="chatbot-toggle-container"> 724 <button id="chatbot-toggle"> 725 <img src="/Content/Templates/images/chat_icon.png" height="60" width="60" /> 726 </button> 727</div> 728 729<!-- Chat Window --> 730<div id="chat-window"> 731 <!-- Chat Header --> 732 <div class="chat-header"> 733 <div class="chat-header-content"> 734 <h3 class="chat-header-title">Rock Solid Forming, Inc. Assistant</h3> 735 </div>
736 <button id="close-chat" class="chat-close-btn" aria-label="Close chat"> 737 <i class="fas fa-times"></i> 738 </button> 739 </div> 740 741 <!-- Chat Messages --> 742 <div id="chat-messages"> 743 <div class="chat-message-container"> 744 <div class="chat-message"> 745 Hi there! How can we help you today? 746 </div> 747 </div> 748 </div> 749 750 <div id="disclaimer-info"> 751 <div class="disclaimer-content"> 752 <div class="disclaimer-text"> 753 AI Responses not guaranteed; verify before reliance 754 </div> 755 </div> 756 </div> 757 758 <!-- Cost Display --> 759 <div id="cost-info"> 760 <div class="cost-info-row"> 761 <div class="cost-info-left"> 762 <i class="fas fa-dollar-sign fa-xs cost-icon"></i> 763 <span id="cost-display">Cost: $0.00000</span> 764 </div> 765 <span id="percentage-display">0.0% of $0.02</span> 766 </div> 767 <div class="cost-progress-bar"> 768 <div id="cost-progress"></div> 769 </div> 770 </div> 771 772 <!-- Chat Input --> 773 <div class="chat-input-container"> 774 <form id="chat-form"> 775 <input type="text" id="chat-input" placeholder="Type your message..." /> 776 <button type="submit" id="send-message"> 777 <i class="fas fa-paper-plane"></i> 778 </button> 779 </form> 780 <div id="character-count">0/250</div> 781 <div id="character-limit-message" style="display: none; color: #dc2626; font-size: 12px; margin-top: 4px;"> 782 Your message is too long. Please limit your question to 250 characters or less. 783 </div> 784 </div> 785</div> 786 787<!-- Contact Form Window --> 788<div id="contact-form-window"> 789 <div class="contact-form-header"> 790 <h3 class="contact-form-title">Contact Form</h3> 791 <button id="close-contact-form" class="contact-form-close-btn" aria-label="Close contact form"> 792 <i class="fas fa-times"></i> 793 </button> 794 </div> 795 796 <div class="contact-form-content"> 797 <form id="chatbot-contact-form"> 798 <input type="hidden" id="chat_captcha_alt" name="chat_captcha_alt" value="vqjL47kwChn4d2RDREif+EQPyhiB1j8PLbHECPXnCr0xU7rYp/FEn4xmF7jNMQ0O" /> 799 <input type="hidden" id="chat_g-Recaptcha-Response-Contact" name="chat_g-Recaptcha-Response-Contact" value="" /> 800 <input type="hidden" name="chat_aid" value="f10de26d-37e5-4621-b687-1879956d6d61" /> 801 <input type="hidden" name="chat_contactSubject" value="FrontDesk™ Contact Form" /> 802 <input type="hidden" name="chat_contactAddress" value="" /> 803 <input type="hidden" name="chat_contactZip" value="" /> 804 <input type="hidden" name="chat_contactCity" value="" /> 805 <input type="hidden" name="chat_contactState" value="" /> 806 807 <div class="ai-liability mt-1 mb-1">(Messages sent through this site are subject to third party moderation and are not encrypted or secure. We always try to respond as soon as possible, but off-hours inquiries may take us a little longer to reply.)</div> 808 809 <div class="form-group"> 810 <label for="chat_contactName" class="form-label">Name *</label> 811 <input type="text" id="chat_contactName" name="chat_contactName" class="form-input" required /> 812 </div> 813 814 <div class="form-group"> 815 <label for="chat_contactEmail" class="form-label">Email *</label> 816 <input type="email" id="chat_contactEmail" name="chat_contactEmail" class="form-input" required /> 817 </div> 818 819 <div class="form-group"> 820 <label for="chat_contactPhone" class="form-label">Phone</label> 821 <input type="tel" id="chat_contactPhone" name="chat_contactPhone" class="form-input" /> 822 </div> 823 824 <div class="form-group"> 825 <label for="chat_contactMessage" class="form-label">Message *</label> 826 <textarea id="chat_contactMessage" name="chat_contactMessage" rows="4" class="form-textarea" required></textarea> 827 </div> 828 829 <div id="classicCaptcha" class="d-flex flex-column"> 830 <div class="g-recaptcha ml-auto mb-3 captcha-responsive" data-sitekey="6LeprWcUAAAAAF533Xd5p6kCrnRs0z4ceJieYgeO"></div> 831 </div> 832 833 <!-- Sticky footer section with reCAPTCHA disclaimer and submit button --> 834 <div class="form-sticky-footer"> 835 <div class="grecaptcha-info"> 836 This site is protected by reCAPTCHA and the Google 837 <a href="https://policies.google.com/privacy">Privacy Policy</a> and 838 <a href="https://policies.google.com/terms">Terms of Service</a> apply. 839 </div> 840 <button type="submit" id="contact-submit-btn" class="form-submit-btn">Submit</button> 841 </div> 842 </form> 843 </div> 844</div> 845
846<script> 847 // Wrap entire chatbot functionality in a namespace to prevent conflicts 848 window.ChatbotModule = (function() { 849 'use strict'; 850 851 // Private variables 852 let isOpen = false; 853 let contactFormOpen = false; 854 let messages = [ 855 { role: 'assistant', content: 'Hi there! How can we help you today?' } 856 ]; 857 let isLoading = false; 858 let showContactForm = false; 859 let limitReached = false; 860 let apiCost = 0; 861 const maxCost = 0.5; 862 const maxQuestionLength = 250; 863 864 // Private element references 865 let $chatbotToggle; 866 let $chatWindow; 867 let $contactFormWindow; 868 let $closeChat; 869 let $closeContactForm; 870 let $chatMessages; 871 let $chatForm; 872 let $chatInput; 873 let $sendMessage; 874 let $characterCount; 875 let $costDisplay; 876 let $percentageDisplay; 877 let $costProgress; 878 let $contactForm; 879 880 // Initialize element references 881 function initializeElements() { 882 $chatbotToggle = $('#chatbot-toggle'); 883 $chatWindow = $('#chat-window'); 884 $contactFormWindow = $('#contact-form-window'); 885 $closeChat = $('#close-chat'); 886 $closeContactForm = $('#close-contact-form'); 887 $chatMessages = $('#chat-messages'); 888 $chatForm = $('#chat-form'); 889 $chatInput = $('#chat-input'); 890 $sendMessage = $('#send-message'); 891 $characterCount = $('#character-count'); 892 $costDisplay = $('#cost-display'); 893 $percentageDisplay = $('#percentage-display'); 894 $costProgress = $('#cost-progress'); 895 $contactForm = $('#chatbot-contact-form'); 896 } 897 898 // Initialize 899 function init() { 900 // Initialize element references 901 initializeElements(); 902 903 // Load cost from localStorage 904 const savedCost = localStorage.getItem('api_cost'); 905 if (savedCost) { 906 apiCost = parseFloat(savedCost); 907 908 // Check if cost limit is reached 909 if (apiCost >= maxCost) { 910 limitReached = true; 911 updateChatInputState(); 912 } 913 } 914 915 // Initialize character count display 916 updateCharacterCount(); 917 918 document.documentElement.style.setProperty('--adaptive-text-color', '#374151'); 919 document.documentElement.style.setProperty('--image-invert', 'none'); 920 921 setTimeout(updateTextColors, 100); 922 923 // Ensure chatbot stays on top even when modals are opened 924 $(document).on('shown.bs.modal', function (e) { 925 // Temporarily boost chatbot z-index when modals are shown 926 $('.chatbot-toggle-container').css('z-index', '2147483647'); 927 $('#chat-window').css('z-index', '2147483646'); 928 $('#contact-form-window').css('z-index', '2147483645'); 929 }); 930 931 // Set up event listeners 932 setupEventListeners(); 933 } 934 935 // Event Listeners with namespacing 936 function setupEventListeners() { 937 // Remove any existing chatbot event listeners to prevent duplicates 938 $chatbotToggle.off('.chatbot'); 939 $closeChat.off('.chatbot'); 940 $closeContactForm.off('.chatbot'); 941 $chatForm.off('.chatbot'); 942 $chatInput.off('.chatbot'); 943 $contactForm.off('.chatbot'); 944 945 // Toggle chat window 946 $chatbotToggle.on('click.chatbot', function(e) { 947 e.preventDefault(); 948 e.stopPropagation(); 949 toggleChat(); 950 }); 951 952 $closeChat.on('click.chatbot', function(e) { 953 e.preventDefault(); 954 e.stopPropagation(); 955 closeChat(); 956 }); 957 958 $closeContactForm.on('click.chatbot', function(e) { 959 e.preventDefault(); 960 e.stopPropagation(); 961 hideContactForm(); 962 }); 963 964 // Send message 965 $chatForm.on('submit.chatbot', function (e) { 966 e.preventDefault(); 967 e.stopPropagation(); 968 969 // Check if submit button is disabled 970 if ($sendMessage.prop('disabled') || $sendMessage.hasClass('disabled')) { 971 return false; 972 } 973 974 sendMessage(); 975 }); 976 977 // Character count - update on every keystroke 978 $chatInput.on('input.chatbot', function(e) { 979 // Only handle if this is specifically the chatbot input 980 if ($(this).attr('id') === 'chat-input') { 981 updateCharacterCount(); 982 } 983 }); 984 985 // Also check on keyup to catch paste events 986 $chatInput.on('keyup.chatbot paste.chatbot', function(e) { 987 // Only handle if this is specifically the chatbot input 988 if ($(this).attr('id') === 'chat-input') { 989 setTimeout(updateCharacterCount, 10); 990 } 991 }); 992 993 // Handle "Enter" key for sending messages 994 $chatInput.on('keydown.chatbot', function (e) { 995 // Only handle if this is specifically the chatbot input 996 if ($(this).attr('id') !== 'chat-input') { 997 return; 998 } 999 1000 if (e.key === 'Enter' && !e.shiftKey && !limitReached) { 1001 e.preventDefault(); 1002 e.stopImmediatePropagation(); 1003 1004 // Check if submit button is disabled 1005 if ($sendMessage.prop('disabled') || $sendMessage.hasClass('disabled')) { 1006 return false; 1007 } 1008 1009 sendMessage(); 1010 return; 1011 } 1012 }); 1013 1014 // Contact form submission 1015 $contactForm.on('submit.chatbot', function (e) { 1016 e.preventDefault(); 1017 e.stopPropagation(); 1018 submitContactForm(); 1019 }); 1020 } 1021 1022 // Toggle chat window 1023 function toggleChat() { 1024 if (!isOpen) { 1025 // Opening the chat 1026 isOpen = true; 1027 1028 // Hide toggle button on both mobile and desktop 1029 $('.chatbot-toggle-container').addClass('hide-toggle'); 1030 1031 // First show the window (still off-screen) 1032 $chatWindow.show(); 1033 1034 // Force a reflow to ensure the transition will work 1035 $chatWindow[0].offsetHeight; 1036 1037 // Then add the class that triggers the slide-in animation 1038 setTimeout(function () { 1039 $chatWindow.addClass('show'); 1040 scrollToBottom(); 1041 }, 10); 1042 } else { 1043 // Closing the chat 1044 isOpen = false; 1045 1046 // Show toggle button again 1047 $('.chatbot-toggle-container').removeClass('hide-toggle'); 1048 1049 // Remove the show class to trigger slide-out animation 1050 $chatWindow.removeClass('show'); 1051 1052 // Hide the window after the animation completes 1053 setTimeout(function () { 1054 $chatWindow.hide(); 1055 }, 300); // Match the CSS transition duration 1056 1057 // Also hide contact form if open 1058 if (contactFormOpen) { 1059 hideContactForm(); 1060 } 1061 } 1062 } 1063 1064 // Close chat window 1065 function closeChat() { 1066 if (isOpen) { 1067 isOpen = false; 1068 1069 // Show toggle button again 1070 $('.chatbot-toggle-container').removeClass('hide-toggle'); 1071 1072 // Trigger slide-out animation 1073 $chatWindow.removeClass('show'); 1074 1075 // Hide after animation 1076 setTimeout(function () { 1077 $chatWindow.hide(); 1078 }, 300); 1079 1080 if (contactFormOpen) { 1081 hideContactForm(); 1082 } 1083 } 1084 } 1085 1086 // Update character count and submit button state 1087 function updateCharacterCount() { 1088 const count = $chatInput.val().length; 1089 const remaining = maxQuestionLength - count; 1090 const $limitMessage = $('#character-limit-message'); 1091 1092 if (count > maxQuestionLength) { 1093 // Over limit - disable submit button and show message 1094 $characterCount.text(`${count}/${maxQuestionLength} (${Math.abs(remaining)} over limit)`).css('color', '#dc2626'); 1095 $limitMessage.text(`Your message is too long. Please limit your question to ${maxQuestionLength} characters or less. Your message is currently ${count} characters.`).show(); 1096 $chatInput.addClass('over-limit'); 1097 1098 // Disable submit button with proper styling 1099 $sendMessage.prop('disabled', true).addClass('disabled'); 1100 $sendMessage.css({ 1101 'background-color': '#9ca3af', 1102 'color': '#6b7280', 1103 'cursor': 'not-allowed', 1104 'opacity': '0.5', 1105 'pointer-events': 'none' 1106 }); 1107 1108 } else if (remaining <= 20) { 1109 // Close to limit - show warning but allow submit 1110 $characterCount.text(`${count}/${maxQuestionLength}`).css('color', '#f59e0b'); 1111 $limitMessage.hide(); 1112 $chatInput.removeClass('over-limit'); 1113 1114 // Enable submit button and reset styling 1115 $sendMessage.prop('disabled', false).removeClass('disabled'); 1116 $sendMessage.css({ 1117 'background-color': '', 1118 'color': '', 1119 'cursor': '', 1120 'opacity': '', 1121 'pointer-events': '' 1122 }); 1123 1124 } else {
1125 // Within limit - normal state 1126 $characterCount.text(`${count}/${maxQuestionLength}`).css('color', '#6b7280'); 1127 $limitMessage.hide(); 1128 $chatInput.removeClass('over-limit'); 1129 1130 // Enable submit button and reset styling 1131 $sendMessage.prop('disabled', false).removeClass('disabled'); 1132 $sendMessage.css({ 1133 'background-color': '', 1134 'color': '', 1135 'cursor': '', 1136 'opacity': '', 1137 'pointer-events': '' 1138 }); 1139 } 1140 } 1141 1142 // Send message 1143 function sendMessage() { 1144 const message = $chatInput.val().trim(); 1145 if (!message || isLoading || limitReached) return; 1146 1147 // Double check character limit including whitespace 1148 if (message.length > maxQuestionLength) { 1149 appendMessage('assistant', `Your message is too long. Please limit your question to ${maxQuestionLength} characters or less. Your message is currently ${message.length} characters.`); 1150 return; 1151 } 1152 1153 // Add user message to chat 1154 messages.push({ role: 'user', content: message }); 1155 appendMessage('user', message); 1156 1157 // Clear input 1158 $chatInput.val(''); 1159 updateCharacterCount(); 1160 1161 // Send to API 1162 fetchChatResponse(messages); 1163 } 1164 1165 // Fetch chat response from API 1166 function fetchChatResponse(messages) { 1167 isLoading = true; 1168 showLoadingIndicator(); 1169 1170 $.ajax({
1171 url: '/Home/Chat', 1172 type: 'POST', 1173 contentType: 'application/json', 1174 data: JSON.stringify({ 1175 Messages: messages, 1176 ProfileId: '16169' 1177 }), 1178 headers: { 1179 'RequestVerificationToken': $('input[name="__RequestVerificationToken"]').val() 1180 }, 1181 success: function (response) { 1182 hideLoadingIndicator(); 1183 1184 if (response.error) { 1185 appendMessage('assistant', `Error: ${response.error}`); 1186 console.error('API Error:', response.error); 1187 isLoading = false; 1188 return; 1189 } 1190 1191 // Update cost 1192 apiCost = response.Cost; 1193 localStorage.setItem('api_cost', apiCost.toString()); 1194 1195 // Check if limit reached 1196 if (response.LimitReached) { 1197 limitReached = true; 1198 showContactFormWindow(); 1199 appendMessage('assistant', 'Our AI chat has reached its capacity. Please use the contact form to get in touch directly with us.'); 1200 } else { 1201 // Check if response contains contact form trigger 1202 if (response.Message.includes('<start_contact_form>')) { 1203 const cleanResponse = response.Message.replace('<start_contact_form>', ''); 1204 appendMessage('assistant', cleanResponse); 1205 showContactFormWindow(); 1206 } else { 1207 appendMessage('assistant', response.Message); 1208 } 1209 1210 // Add response to messages array 1211 messages.push({ role: 'assistant', content: response.Message }); 1212 } 1213 1214 isLoading = false; 1215 updateChatInputState(); 1216 }, 1217 error: function (xhr, status, error) { 1218 hideLoadingIndicator(); 1219 appendMessage('assistant', 'Sorry, I encountered an error. Please try again later.'); 1220 console.error('AJAX Error:', error); 1221 isLoading = false; 1222 } 1223 }); 1224 } 1225 1226 // Append message to chat 1227 function appendMessage(role, content) { 1228 content = convertMarkdownLinksToHTML(content); 1229 const rootStyles = getComputedStyle(document.documentElement); 1230 const customerColor = rootStyles.getPropertyValue('--customer_color').trim(); 1231 const adaptivefontColor = rootStyles.getPropertyValue('--adaptive-text-color').trim(); 1232 1233 const messageClass = role === 'user' ? 'flex-end' : 'flex-start'; 1234 const bgColor = role === 'user' ? customerColor : 'white'; 1235 const textColor = role === 'user' ? adaptivefontColor : '#374151'; 1236 const borderRadius = role === 'user' ? 'border-bottom-right-radius: 0;' : 'border-bottom-left-radius: 0;'; 1237 const border = role === 'user' ? '' : 'border: 1px solid #e5e7eb;'; 1238 1239 const messageHtml = ` 1240 <div style="display: flex; justify-content: ${messageClass}; margin-bottom: 16px;"> 1241 <div style="max-width: 75%; padding: 12px; border-radius: 8px; background-color: ${bgColor}; ${border} ${borderRadius} color: ${textColor}; word-wrap: break-word; overflow-wrap: break-word;"> 1242 ${content} 1243 </div> 1244 </div> 1245 `; 1246 1247 $chatMessages.append(messageHtml); 1248 scrollToBottom(); 1249 } 1250 1251 // Show loading indicator 1252 function showLoadingIndicator() { 1253 const loadingHtml = ` 1254 <div id="loading-indicator" style="display: flex; justify-content: flex-start; margin-bottom: 16px;"> 1255 <div style="max-width: 75%; padding: 12px; border-radius: 8px; background-color: white; border: 1px solid #e5e7eb; color: #374151; display: flex; gap: 4px;"> 1256 <div class="loading-dot" style="width: 8px; height: 8px; background-color: #9ca3af; border-radius: 50%;"></div> 1257 <div class="loading-dot" style="width: 8px; height: 8px; background-color: #9ca3af; border-radius: 50%;"></div> 1258 <div class="loading-dot" style="width: 8px; height: 8px; background-color: #9ca3af; border-radius: 50%;"></div> 1259 </div> 1260 </div> 1261 `; 1262 1263 $chatMessages.append(loadingHtml); 1264 animateLoadingDots(); 1265 scrollToBottom(); 1266 1267 // Disable input while loading 1268 $chatInput.prop('disabled', true); 1269 $sendMessage.prop('disabled', true); 1270 } 1271 1272 // Animate loading dots 1273 function animateLoadingDots() { 1274 if (!$("#loading-indicator").length) return; 1275 1276 $(".loading-dot").each(function (index) { 1277 var $dot = $(this); 1278 setTimeout(function () { 1279 $dot.animate({ opacity: 0.3 }, 300) 1280 .animate({ opacity: 1 }, 300); 1281 }, index * 200); 1282 }); 1283 1284 setTimeout(function () { 1285 animateLoadingDots(); 1286 }, 900); 1287 } 1288 1289 // Hide loading indicator 1290 function hideLoadingIndicator() { 1291 $('#loading-indicator').remove(); 1292 1293 // Enable input after loading (unless limit reached) 1294 if (!limitReached) { 1295 $chatInput.prop('disabled', false); 1296 updateCharacterCount(); // This will set the correct submit button state 1297 } 1298 } 1299 1300 // Scroll to bottom of chat 1301 function scrollToBottom() { 1302 $chatMessages.scrollTop($chatMessages[0].scrollHeight); 1303 } 1304 1305 // Update cost display 1306 function updateCostDisplay() { 1307 $("#cost-info").show(); 1308 $costDisplay.text(`Cost: $${apiCost.toFixed(5)}`); 1309 1310 const percentage = (apiCost / maxCost) * 100; 1311 $percentageDisplay.text(`${percentage.toFixed(1)}% of $${maxCost.toFixed(2)}`); 1312 1313 $costProgress.css('width', `${percentage}%`); 1314
1315 // Change progress bar color if near limit 1316 if (percentage > 80) { 1317 $costProgress.css("background-color", "#dc2626"); 1318 } else { 1319 $costProgress.css("background-color", "#4f46e5"); 1320 } 1321 1322 // Update chat input state 1323 updateChatInputState(); 1324 } 1325 1326 // Update chat input state based on limit 1327 function updateChatInputState() { 1328 if (limitReached) { 1329 $chatInput.prop('disabled', true); 1330 $sendMessage.prop('disabled', true); 1331 $chatInput.attr('placeholder', 'Chat Unavailable.'); 1332 } else { 1333 $chatInput.prop('disabled', false); 1334 updateCharacterCount(); // This will set the correct submit button state 1335 $chatInput.attr('placeholder', 'Type your message...'); 1336 } 1337 } 1338 1339 // Show contact form 1340 function showContactFormWindow() { 1341 if (!contactFormOpen) { 1342 contactFormOpen = true; 1343 1344 // Hide toggle button when contact form opens 1345 $('.chatbot-toggle-container').addClass('hide-toggle'); 1346 1347 if (isMobile()) { 1348 // On mobile, slide out chat and slide in contact form 1349 $chatWindow.removeClass('show'); 1350 setTimeout(function () { 1351 $chatWindow.hide(); 1352 $contactFormWindow.show(); 1353 // Force reflow 1354 $contactFormWindow[0].offsetHeight; 1355 // Add show class for slide animation 1356 setTimeout(function () { 1357 $contactFormWindow.addClass('show'); 1358 $('.contact-form-content').scrollTop(0); 1359 }, 10); 1360 }, 300); 1361 } else { 1362 // Desktop - slide chat to left and slide in contact form 1363 $chatWindow.animate({ right: '390px' }, 300); 1364 $contactFormWindow.show(); 1365 setTimeout(function () { 1366 $contactFormWindow.animate({ right: '20px' }, 300); 1367 }, 50); 1368 } 1369 } 1370 } 1371 1372 // Hide contact form - slides out and chat returns to original position 1373 function hideContactForm() { 1374 if (contactFormOpen) { 1375 contactFormOpen = false; 1376 1377 if (isMobile()) { 1378 // Slide out contact form 1379 $contactFormWindow.removeClass('show'); 1380 1381 setTimeout(function () { 1382 $contactFormWindow.hide(); 1383 // Show chat window again if it was open 1384 if (isOpen) { 1385 $chatWindow.show(); 1386 // Force reflow 1387 $chatWindow[0].offsetHeight; 1388 setTimeout(function () { 1389 $chatWindow.addClass('show'); 1390 }, 10); 1391 } else { 1392 // If chat is not open, show toggle button 1393 $('.chatbot-toggle-container').removeClass('hide-toggle'); 1394 } 1395 }, 300); 1396 } else { 1397 // Desktop - animate back 1398 $contactFormWindow.animate({ right: '-370px' }, 300, function () { 1399 $contactFormWindow.hide(); 1400 }); 1401 $chatWindow.animate({ right: '20px' }, 300); 1402 1403 // Only show toggle if both windows are closed 1404 if (!isOpen) { 1405 $('.chatbot-toggle-container').removeClass('hide-toggle'); 1406 } 1407 } 1408 } 1409 } 1410
1411 // Submit contact form 1412 function submitContactForm() { 1413 const name = $('#chat_contactName').val(); 1414 const email = $('#chat_contactEmail').val(); 1415 const phone = $('#chat_contactPhone').val(); 1416 const message = $('#chat_contactMessage').val(); 1417 1418 // Basic validation 1419 if (!name || !email || !message) { 1420 alert('Please fill out all required fields.'); 1421 return; 1422 } 1423 1424 // Email validation 1425 const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; 1426 if (!emailRegex.test(email)) { 1427 alert('Please enter a valid email address.'); 1428 return; 1429 } 1430 1431 // Message length validation 1432 if (message.length < 10) { 1433 alert('Your message must be at least 10 characters long.'); 1434 return; 1435 } 1436 1437 // Disable form during submission 1438 $('#contact-submit-btn').prop('disabled', true).text('Sending...'); 1439 1440 var form = $("#chatbot-contact-form"); 1441 1442 // Execute reCAPTCHA before submitting 1443 grecaptcha.ready(function() { 1444 grecaptcha.execute('6LcokacUAAAAAH_snk3O5bpdYwjpC5XAuXNLrJLH', {action: 'contact_us'}).then(function(token) { 1445 // Set the reCAPTCHA token 1446 $("#chat_g-Recaptcha-Response-Contact").val(token); 1447 1448 // Submit the form with reCAPTCHA token 1449 $.ajax({
1450 url: "/Home/ChatContactPost", 1451 type: 'POST', 1452 data: form.serialize(), 1453 success: function (response) { 1454 if (typeof response === 'string' && response.indexOf('{"message":') != -1) { 1455 var jsonData = JSON.parse(response); 1456 if (jsonData.success) { 1457 appendMessage('assistant', 'Thanks for providing your information! We will get back to you soon.'); 1458 hideContactForm(); 1459 1460 // Reset the form 1461 $('#chat_contactName').val(''); 1462 $('#chat_contactEmail').val(''); 1463 $('#chat_contactPhone').val(''); 1464 $('#chat_contactMessage').val(''); 1465 } else { 1466 alert(jsonData.message || 'There was an error processing your request.'); 1467 } 1468 } else if (response.success) { 1469 appendMessage('assistant', 'Thanks for providing your information! We will get back to you soon.'); 1470 hideContactForm(); 1471 1472 // Reset the form 1473 $('#chat_contactName').val(''); 1474 $('#chat_contactEmail').val(''); 1475 $('#chat_contactPhone').val(''); 1476 $('#chat_contactMessage').val(''); 1477 1478 } else { 1479 alert(response.message || 'There was an error processing your request.'); 1480 } 1481 1482 $('#contact-submit-btn').prop('disabled', false).text('Submit'); 1483 }, 1484 error: function (xhr, status, error) { 1485 alert('Failed to submit your information. Please try again.'); 1486 console.error('AJAX Error:', error); 1487 $('#contact-submit-btn').prop('disabled', false).text('Submit'); 1488 } 1489 }); 1490 }); 1491 }); 1492 } 1493 1494 function convertMarkdownLinksToHTML(text) { 1495 return text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, linkText, url) => { 1496 // Add target="_blank" for http/https links 1497 if (url.startsWith('http://') || url.startsWith('https://')) { 1498 return `<a href="${url}" target="_blank" rel="noopener noreferrer">${linkText}</a>`; 1499 } 1500 // Keep tel: and mailto: links as-is 1501 return `<a href="${url}">${linkText}</a>`; 1502 }); 1503 } 1504 1505 function getLuminance(hex) { 1506 // Remove # if present 1507 hex = hex.replace('#', ''); 1508 1509 // Convert hex to RGB 1510 const r = parseInt(hex.substr(0, 2), 16) / 255; 1511 const g = parseInt(hex.substr(2, 2), 16) / 255; 1512 const b = parseInt(hex.substr(4, 2), 16) / 255; 1513 1514 // Calculate relative luminance 1515 const rLum = r <= 0.03928 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4); 1516 const gLum = g <= 0.03928 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4); 1517 const bLum = b <= 0.03928 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4); 1518 1519 return 0.2126 * rLum + 0.7152 * gLum + 0.0722 * bLum; 1520 } 1521 1522 function isLightColor(hex) { 1523 const luminance = getLuminance(hex); 1524 return luminance > 0.5; // Threshold for light vs dark 1525 } 1526 1527 function updateTextColors() { 1528 const rootStyles = getComputedStyle(document.documentElement); 1529 let customerColor = rootStyles.getPropertyValue('--customer_color').trim(); 1530 1531 // Check if customer color is not set or is empty 1532 if (!customerColor || customerColor === '') { 1533 const defaultColor = '#f3f3f3'; 1534 1535 // Set the default customer color 1536 document.documentElement.style.setProperty('--customer_color', defaultColor); 1537 customerColor = defaultColor; 1538 } 1539 1540 const isLight = isLightColor(customerColor); 1541 const textColor = isLight ? '#000000b3' : '#ffffff'; 1542 1543 const $chatbotToggle = $('#chatbot-toggle'); 1544 if (isLight) { 1545 $chatbotToggle.removeClass('dark-background').addClass('light-background'); 1546 } else { 1547 $chatbotToggle.removeClass('light-background').addClass('dark-background'); 1548 } 1549 1550 // Update the CSS custom property 1551 document.documentElement.style.setProperty('--adaptive-text-color', textColor); 1552 } 1553 1554 function isMobile() { 1555 return window.innerWidth <= 768; 1556 } 1557 1558 // Public interface 1559 return { 1560 init: init 1561 }; 1562 })(); 1563 1564 // Initialize the chatbot module when document is ready 1565 $(document).ready(function () { 1566 if (window.ChatbotModule) {
1567 window.ChatbotModule.init(); 1568 } 1569 }); 1570</script>
1570 1571<div id="loadingImg" class="loader" style="display:none !important"> 1572 <i class="fas fa-circle"></i> 1573 <i class="fas fa-circle"></i> 1574 <i class="fas fa-circle"></i> 1575</div> 1576
1577<script type="text/javascript"> 1578 var isMobile = false; 1579 var isTablet = false; 1580 var itemsVisible = false; // false indicates that items are initially hidden 1581 var pageSize = 5; 1582 var pageIndex = 0; 1583 var hasbanner = true; 1584 var keepPostUp = false; 1585 var listOfSites = ""; 1586 var testimonialPageBackgroundColor = '#000000'; 1587 var numOfPosts = 5; 1588 var postsPageIndex = 0; 1589 var geocode = null; 1590 var selectedServiceId = null; 1591 var servicesRendered = false; 1592 var aboutRendered = false; 1593 var testimonialsRendered = false; 1594 var contactRendered = false; 1595 var photosRendered = false; 1596 var faqRendered = false; 1597 var locationsRendered = false; 1598 var additionalContentRendered = false; 1599 var isSubmittingReview = false; //global flag to track submission 1600 1601 $(document).ready(function () { 1602 if ($("#contact_us_phone").length > 0) { 1603 var cleave = new Cleave('#contact_us_phone', { 1604 phone: true, 1605 phoneRegionCode: 'US' 1606 }); 1607 } 1608 1609 checkMobile(); 1610 checkTablet(); 1611 checkHashRoute(); 1612 PauseVideoPlayer(); 1613 1614 if(hasbanner){ 1615 GetPostsBanner(); 1616 } 1617 1618 $(window).on('hashchange', function(){ 1619 checkHashRoute(); 1620 }); 1621 1622 var navMain = $("#navbarToggler"); 1623 navMain.on("click", "a", null, function () { 1624 navMain.collapse('hide'); 1625 }); 1626 1627 document.addEventListener("visibilitychange", function () { 1628 if (document["hidden"]) { 1629 PauseVideoPlayer(); 1630 } 1631 }); 1632 1633 $(window).scroll(function () { 1634 var scroll = $(window).scrollTop(); 1635 if (('True' == 'False') || ('True' == 'True' && window.outerWidth > 767.98)) { 1636 if (scroll > 150){ 1637 $(".content").addClass("page-section-adjust"); 1638 $(".nav-logo").addClass("nav-logo-adjust"); 1639 $(".nav-location").addClass("nav-item-adjust"); 1640 } 1641 else if(scroll <= 150){ 1642 $(".content").removeClass("page-section-adjust"); 1643 $(".nav-logo").removeClass("nav-logo-adjust"); 1644 $(".nav-location").removeClass("nav-item-adjust"); 1645 } 1646 } 1647 }); 1648 1649 $(document).on("click", "#btnContactPost", function (e) { 1650 $("#btnContactPost").prop('disabled', true); 1651 $("#btnContactPost").css('font-weight', 'normal'); 1652 contactUsPost(e); 1653 }); 1654 1655 var analyticsCategory = "Desktop"; 1656 if (isMobile == true) { 1657 analyticsCategory = "Mobile"; 1658 if ($(window).width() < 576) { 1659 if ($("#headernav").hasClass('justify-content-between')) { 1660 $("#headernav").removeClass('justify-content-between'); 1661 } 1662 if (!$("#headernav").hasClass('justify-content-center')) { 1663 $("#headernav").addClass('justify-content-center'); 1664 } 1665 if ($("#footer-address").hasClass('justify-content-center')) { 1666 $("#footer-address").removeClass('justify-content-center'); 1667 } 1668 if ($("#footer-hours").hasClass('justify-content-center')) { 1669 $("#footer-hours").removeClass('justify-content-center'); 1670 } 1671 if ($("#footer-sitenav").hasClass('justify-content-center')) { 1672 $("#footer-sitenav").removeClass('justify-content-center'); 1673 } 1674 } 1675 } 1676 1677 if (testimonialPageBackgroundColor != null && testimonialPageBackgroundColor != '' && testimonialPageBackgroundColor != "#F5F5F5") { 1678 //set footer background color 1679 $('.custom-background-color').css('background-color', testimonialPageBackgroundColor); 1680 } 1681 else { 1682 //set footer background color to white 1683 var white = "#FFFFFF"; 1684 $('.custom-background-color').css('background-color', white); 1685 } 1686 1687 $("#HomePageNavigationLink").click(function(e){
1688 $('html, body').animate({ scrollTop: 0 }, 'fast'); 1689 }); 1690 1691 $(".business-name").click(function(e){ 1692 $('html, body').animate({ scrollTop: 0 }, 'fast'); 1693 }); 1694 1695 if(numOfPosts > 0){ 1696 GetPostsData(0); 1697 } 1698 1699 $(document).on('click', "#nextPosts", function (e) { 1700 ScrollForPosts(e); 1701 GetPostsData(1); 1702 }); 1703 1704 $(document).on('click', "#prevPosts", function (e) { 1705 ScrollForPosts(e); 1706 GetPostsData(-1); 1707 }); 1708 1709 $(document).on('click', "#postReview", function (e) { 1710 e.preventDefault(); 1711 e.stopPropagation(); 1712 1713 if (isSubmittingReview) { 1714 console.log('Review submission already in progress - blocking duplicate submission'); 1715 return false; 1716 } 1717 1718 var $button = $(this); 1719 var $form = $("#postReviewForm"); 1720 var $modal = $("#reviewFormSection"); 1721 1722 isSubmittingReview = true; 1723 console.log('Starting review submission'); 1724 1725 setProcessingState($button, $form, true); 1726 1727 var alertType = "success"; 1728 1729 if(validateReview("#postReview")) { 1730 console.log('Review validation passed'); 1731 ShowLoading(); 1732 1733 grecaptcha.ready(function() { 1734 grecaptcha.execute('6LcokacUAAAAAH_snk3O5bpdYwjpC5XAuXNLrJLH', {action: 'testimonial_review'}).then(function(token) { 1735 $("#g-Recaptcha-Response-Review").attr("value", token); 1736 1737 var rating = null; 1738 var selectElement = document.getElementById('starRating'); 1739 var selectRating = selectElement ? selectElement.value : null; 1740 var selectedOption = $('#starRating option:selected').val(); 1741 var barRating = null; 1742 var reviewerAlias = $('#review_alias').val() || ''; 1743 var reviewHeading = $('#review_heading').val() || ''; 1744 var antiForgeryToken = $('input[name="__RequestVerificationToken"]').val(); 1745 var recaptchaToken = $('#g-Recaptcha-Response-Review').val(); 1746 var timeToPost = $('#phoneNumber_alternative').val(); 1747 1748 var reviewText = $('#review').val() || ''; 1749 var profileId = 16169; 1750 1751 console.log('Trying to get rating value...'); 1752 1753 var selectRating = $('#starRating')[0] ? $('#starRating')[0].value : null; 1754 console.log('Select element direct value:', selectRating); 1755 1756 var jqueryRating = $('#starRating').val(); 1757 console.log('jQuery val() result:', jqueryRating, typeof jqueryRating); 1758 1759 if (typeof $('#starRating').barrating === 'function') { 1760 try { 1761 var barResult = $('#starRating').barrating('show'); 1762 1763 if (barResult && typeof barResult === 'string') { 1764 barRating = barResult; 1765 } else if (barResult && typeof barResult === 'number') { 1766 barRating = barResult.toString(); 1767 } 1768 } catch (e) { 1769 console.log('Error getting barrating value:', e); 1770 } 1771 } 1772 1773 var selectedOption = $('#starRating option:selected').val(); 1774 console.log('Selected option value:', selectedOption); 1775 1776 var jqueryResult = $('#starRating').val(); 1777 var jqueryRating = null; 1778 1779 if (jqueryResult && typeof jqueryResult === 'string') { 1780 jqueryRating = jqueryResult; 1781 } else if (jqueryResult && typeof jqueryResult === 'number') { 1782 jqueryRating = jqueryResult.toString(); 1783 } 1784 1785 if (barRating && barRating !== "" && barRating !== "0") { 1786 rating = barRating; 1787 } else if (selectRating && selectRating !== "" && selectRating !== "0") { 1788 rating = selectRating; 1789 } else if (selectedOption && selectedOption !== "" && selectedOption !== "0") { 1790 rating = selectedOption; 1791 } else if (jqueryRating && jqueryRating !== "" && jqueryRating !== "0") { 1792 rating = jqueryRating; 1793 } 1794 1795 if (rating && typeof rating !== 'string') { 1796 console.log('WARNING: Rating is not a string, converting:', rating, typeof rating);
1797 rating = String(rating); 1798 } 1799 1800 if (rating && typeof rating === 'object') { 1801 console.error('CRITICAL ERROR: Rating is still an object!', rating); 1802 rating = null; 1803 } 1804 1805 console.log('Validation check - rating found:', rating, typeof rating); 1806 console.log('Final rating value:', rating, typeof rating); 1807 1808 console.log('Form values before sending:'); 1809 console.log('- Review text:', reviewText); 1810 console.log('- Rating:', rating); 1811 console.log('- Profile ID:', profileId); 1812 console.log('- Reviewer alias:', reviewerAlias); 1813 console.log('- Review heading:', reviewHeading); 1814 console.log('- Anti-forgery token:', antiForgeryToken ? 'Present' : 'Missing'); 1815 1816 // Build form data 1817 var formDataArray = [ 1818 { name: 'profile_id', value: profileId }, 1819 { name: 'review', value: reviewText }, 1820 { name: 'review_rating', value: rating }, 1821 { name: 'reviewer_alias', value: reviewerAlias }, 1822 { name: 'review_heading', value: reviewHeading }, 1823 { name: '__RequestVerificationToken', value: antiForgeryToken }, 1824 { name: 'g-Recaptcha-Response-Review', value: recaptchaToken }, 1825 { name: 'phoneNumber_alternative', value: timeToPost }, 1826 { name: '_t', value: new Date().getTime() } 1827 ]; 1828 1829 var formData = formDataArray 1830 .filter(item => item.value !== undefined && item.value !== null && item.value !== '') 1831 .map(item => encodeURIComponent(item.name) + '=' + encodeURIComponent(item.value)) 1832 .join('&'); 1833 1834 console.log('Manual form data built:', formData); 1835 1836 $.ajax({
1837 url: "/Home/ReviewPost", 1838 type: "POST", 1839 data: formData, 1840 contentType: "application/x-www-form-urlencoded; charset=UTF-8", 1841 dataType: "json", 1842 success: function(data) { 1843 console.log('Review submission response:', data); 1844 1845 if (!data.message || data.message.trim() === '') { 1846 data.message = data.success ? 1847 'Thank you for your review! It has been submitted successfully.' : 1848 'There was an issue submitting your review. Please check all fields and try again.'; 1849 } 1850 1851 alertType = data.success ? "success" : "warning"; 1852 1853 if (!data.success) { 1854 recaptchaReviewIncomplete(); 1855 showClassicCaptcha("review"); 1856 } 1857 1858 $form.find('.alert').remove(); 1859 1860 var alertHtml = '<div class="alert alert-' + alertType + ' alert-dismissible" role="alert">' + 1861 '<button type="button" class="close" data-dismiss="alert" aria-label="Close">' + 1862 '<span aria-hidden="true">×</span></button>' + 1863 data.message + '</div>'; 1864 1865 $form.prepend(alertHtml); 1866 1867 $modal.find('.modal-body').scrollTop(0); 1868 1869 if(alertType === "success") { 1870 // Auto-hide modal after showing success message 1871 setTimeout(function () { 1872 $modal.modal('hide'); 1873 ClearReviewFields(); 1874 }, 3000); 1875 } 1876 1877 // Fade out alert after delay 1878 setTimeout(function() { 1879 $form.find('.alert').fadeOut(500, function() { 1880 $(this).remove(); 1881 }); 1882 }, 6000); 1883 }, 1884 error: function(xhr, status, error) { 1885 console.error('AJAX Error:', status, error); 1886 console.error('Response Text:', xhr.responseText); 1887 1888 $form.find('.alert').remove(); 1889 var errorHtml = '<div class="alert alert-danger alert-dismissible" role="alert">' + 1890 '<button type="button" class="close" data-dismiss="alert" aria-label="Close">' + 1891 '<span aria-hidden="true">×</span></button>' + 1892 'An error occurred while submitting your review. Please try again.</div>'; 1893 $form.prepend(errorHtml); 1894 1895 $modal.find('.modal-body').scrollTop(0); 1896 }, 1897 complete: function() { 1898 // Reset states 1899 console.log('Resetting processing state'); 1900 setProcessingState($button, $form, false); 1901 HideLoading(); 1902 isSubmittingReview = false; 1903 console.log('Review submission completed'); 1904 } 1905 }); 1906 1907 }).catch(function(error) { 1908 console.error('reCAPTCHA Error:', error); 1909 1910 $form.find('.alert').remove(); 1911 var captchaErrorHtml = '<div class="alert alert-danger alert-dismissible" role="alert">
1911' + 1912 '<button type="button" class="close" data-dismiss="alert" aria-label="Close">' + 1913 '<span aria-hidden="true">×</span></button>' + 1914 'Security verification failed. Please refresh the page and try again.</div>'; 1915 $form.prepend(captchaErrorHtml); 1916 1917 $modal.find('.modal-body').scrollTop(0); 1918 setProcessingState($button, $form, false); 1919 HideLoading(); 1920 isSubmittingReview = false; 1921 }); 1922 }); 1923 } else { 1924 console.log('Review validation failed'); 1925 1926 var missingFields = []; 1927 if (!$('#review').val() || $('#review').val().trim() === '') { 1928 missingFields.push('review text'); 1929 } 1930 1931 var rating = null; 1932 var selectRating = $('#starRating')[0] ? $('#starRating')[0].value : null; 1933 var jqueryRating = $('#starRating').val(); 1934 var selectedOption = $('#starRating option:selected').val(); 1935 var barRating = null; 1936 1937 if (typeof $('#starRating').barrating === 'function') { 1938 try { 1939 barRating = $('#starRating').barrating('show'); 1940 } catch (e) { 1941 // Ignore error 1942 } 1943 } 1944 1945 if (barRating && barRating !== "" && barRating !== "0") { 1946 rating = barRating; 1947 } else if (selectRating && selectRating !== "" && selectRating !== "0") { 1948 rating = selectRating; 1949 } else if (selectedOption && selectedOption !== "" && selectedOption !== "0") { 1950 rating = selectedOption; 1951 } else if (typeof jqueryRating === 'string' && jqueryRating !== "" && jqueryRating !== "0") { 1952 rating = jqueryRating; 1953 } 1954 1955 console.log('Validation check - rating found:', rating); 1956 1957 if (!rating || rating === "" || rating === "0") { 1958 missingFields.push('rating'); 1959 } 1960 1961 var validationMessage = 'Please fill out the following required fields: ' + missingFields.join(' and ') + '.'; 1962 console.log('Validation message:', validationMessage); 1963 1964 $form.find('.alert').remove(); 1965 var validationHtml = '<div class="alert alert-warning alert-dismissible" role="alert">' + 1966 '<button type="button" class="close" data-dismiss="alert" aria-label="Close">' + 1967 '<span aria-hidden="true">×</span></button>' + 1968 validationMessage + '</div>'; 1969 $form.prepend(validationHtml); 1970 1971 $modal.find('.modal-body').scrollTop(0); 1972 setProcessingState($button, $form, false); 1973 isSubmittingReview = false; 1974 1975 return false; 1976 } 1977 }); 1978 1979 $('#reviewFormSection').on('hidden.bs.modal', function () { 1980 console.log('Modal hidden - resetting submission state'); 1981 isSubmittingReview = false; 1982 setProcessingState($("#postReview"), $("#postReviewForm"), false); 1983 $(this).find('.alert').remove(); 1984 }); 1985 1986 $('#reviewFormSection').on('show.bs.modal', function () { 1987 console.log('Modal shown - ensuring clean state'); 1988 isSubmittingReview = false; 1989 setProcessingState($("#postReview"), $("#postReviewForm"), false); 1990 1991 $("#postReview").prop('disabled', false); 1992 $("#postReview").css('font-weight', 'bold'); 1993 1994 setTimeout(function () { 1995 console.log('=== STAR RATING DEBUG ==='); 1996 console.log('Star rating element:', $('#starRating')[0]); 1997 console.log('Star rating jQuery object:', $('#starRating')); 1998 console.log('Star rating direct value:', $('#starRating')[0] ? $('#starRating')[0].value : 'N/A'); 1999 console.log('Star rating jQuery val():', $('#starRating').val()); 2000 console.log('Star rating selected option:', $('#starRating option:selected').val()); 2001 console.log('Barrating plugin available:', typeof $('#starRating').barrating === 'function'); 2002 if (typeof $('#starRating').barrating === 'function') { 2003 try { 2004 console.log('Barrating current value:', $('#starRating').barrating('show')); 2005 } catch (e) { 2006 console.log('Error getting barrating value:', e); 2007 } 2008 } 2009 console.log('=== END STAR RATING DEBUG ==='); 2010 }, 100); 2011 }); 2012 2013 // Prevent modal from closing during submission 2014 $('#reviewFormSection').on('hide.bs.modal', function (e) { 2015 if (isSubmittingReview) { 2016 console.log('Preventing modal close during submission'); 2017 e.preventDefault(); 2018 return false; 2019 } 2020 }); 2021 2022 $('a').on('click', function () { 2023 if (this.id !== "undefined" && this.id !== "") { 2024 gtag('event', 'Link Click', { 2025 'event_category': analyticsCategory, 2026 'event_label': this.id 2027 }); 2028 } 2029 }); 2030 2031 $('#PhoneNav').on('click', function () { 2032 gtag('event', 'Link Click', { 2033 'event_category': analyticsCategory, 2034 'event_label': "CustomerBusinessPhoneLinkPlatinum1" 2035 }); 2036 }); 2037 $('.phoneLinkInput').on('click', function () { 2038 gtag('event', 'Link Click', { 2039 'event_category': analyticsCategory, 2040 'event_label': "CustomerBusinessPhoneLinkPlatinum1-1" 2041 }); 2042 }); 2043 $('#contactPhoneLink').on('click', function () { 2044 gtag('event', 'Link Click', { 2045 'event_category': analyticsCategory, 2046 'event_label': "CustomerBusinessPhoneLinkPlatinum1-2" 2047 }); 2048 }); 2049 $('#contactPrimaryPhoneLink').on('click', function () { 2050 gtag('event', 'Link Click', { 2051 'event_category': analyticsCategory, 2052 'event_label': "CustomerBusinessPhoneLinkPlatinum1-3" 2053 }); 2054 }); 2055 $('#contactSecondaryPhoneLink').on('click', function () { 2056 gtag('event', 'Link Click', { 2057 'event_category': analyticsCategory, 2058 'event_label': "CustomerBusinessPhoneLinkPlatinum1-4" 2059 }); 2060 }); 2061 $('#contactMobilePhoneLink').on('click', function () { 2062 gtag('event', 'Link Click', { 2063 'event_category': analyticsCategory, 2064 'event_label': "CustomerBusinessPhoneLinkPlatinum1-5" 2065 }); 2066 }); 2067 $('#contactFaxPhoneLink').on('click', function () { 2068 gtag('event', 'Link Click', { 2069 'event_category': analyticsCategory, 2070 'event_label': "CustomerBusinessPhoneLinkPlatinum1-6" 2071 }); 2072 }); 2073 $('#contactTollfreePhoneLink').on('click', function () { 2074 gtag('event', 'Link Click', { 2075 'event_category': analyticsCategory, 2076 'event_label': "CustomerBusinessPhoneLinkPlatinum1-7" 2077 }); 2078 }); 2079 $('#contactPhoneC1').on('click', function () { 2080 gtag('event', 'Link Click', { 2081 'event_category': analyticsCategory, 2082 'event_label': "CustomerContactPhoneLinkPlatinum1-8" 2083 }); 2084 }); 2085 $('#contactPhoneC2').on('click', function () { 2086 gtag('event', 'Link Click', { 2087 'event_category': analyticsCategory, 2088 'event_label': "CustomerContactPhoneLinkPlatinum1-9" 2089 }); 2090 }); 2091 $('#contactPhoneC3').on('click', function () { 2092 gtag('event', 'Link Click', { 2093 'event_category': analyticsCategory, 2094 'event_label': "CustomerContactPhoneLinkPlatinum1-10" 2095 }); 2096 }); 2097 $('#contactPhoneC4').on('click', function () { 2098 gtag('event', 'Link Click', { 2099 'event_category': analyticsCategory, 2100 'event_label': "CustomerContactPhoneLinkPlatinum1-11" 2101 }); 2102 }); 2103 $('#contactPhoneC5').on('click', function () { 2104 gtag('event', 'Link Click', { 2105 'event_category': analyticsCategory, 2106 'event_label': "CustomerContactPhoneLinkPlatinum1-12" 2107 }); 2108 }); 2109 $('#contactEmail').on('click', function () { 2110 gtag('event', 'Link Click', { 2111 'event_category': analyticsCategory, 2112 'event_label': "CustomerBusinessEmailLinkPlatinum1" 2113 }); 2114 }); 2115 $('#writeReviewBt').on('click', function () { 2116 gtag('event', 'Button Click', { 2117 'event_category': analyticsCategory, 2118 'event_label': "WriteReviewButtonPlatinum1" 2119 }); 2120 }); 2121 $(document).on('click', '#postReview', function () { 2122 gtag('event', 'Button Click', { 2123 'event_category': analyticsCategory, 2124 'event_label': "PostReviewButtonPlatinum1" 2125 }); 2126 }); 2127 $('#nextReview').on('click', function () { 2128 gtag('event', 'Button Click', { 2129 'event_category': analyticsCategory, 2130 'event_label': "ShowNextReviewsButtonPlatinum1" 2131 }); 2132 }); 2133 $('#prevReview').on('click', function () { 2134 gtag('event', 'Button Click', { 2135 'event_category': analyticsCategory, 2136 'event_label': "ShowPreviousReviewsButtonPlatinum1" 2137 }); 2138 }); 2139 $('#google-social').on('click', function () { 2140 gtag('event', 'Link Click', { 2141 'event_category': analyticsCategory, 2142 'event_label': "CustomerGoogleSocialLinkPlatinum1-1" 2143 }); 2144 }); 2145 $('#facebook-social').on('click', function () { 2146 gtag('event', 'Link Click', { 2147 'event_category': analyticsCategory, 2148 'event_label': "CustomerFacebookSocialLinkPlatinum1-1" 2149 }); 2150 }); 2151 $('#yelp-social').on('click', function () { 2152 gtag('event', 'Link Click', { 2153 'event_category': analyticsCategory, 2154 'event_label': "CustomerYelpSocialLinkPlatinum1-1" 2155 }); 2156 }); 2157 $('#twitter-social').on('click', function () { 2158 gtag('event', 'Link Click', { 2159 'event_category': analyticsCategory, 2160 'event_label': "CustomerTwitterSocialLinkPlatinum1-1" 2161 }); 2162 }); 2163 $('#linkedin-social').on('click', function () { 2164 gtag('event', 'Link Click', { 2165 'event_category': analyticsCategory, 2166 'event_label': "CustomerLinkedinSocialLinkPlatinum1-1" 2167 }); 2168 }); 2169 $('#instagram-social').on('click', function () { 2170 gtag('event', 'Link Click', { 2171 'event_category': analyticsCategory,
2172 'event_label': "CustomerInstagramSocialLinkPlatinum1-1" 2173 }); 2174 }); 2175 $('#google-social-footer').on('click', function () { 2176 gtag('event', 'Link Click', { 2177 'event_category': analyticsCategory, 2178 'event_label': "CustomerGoogleSocialLinkPlatinum1-2" 2179 }); 2180 }); 2181 $('#facebook-social-footer').on('click', function () { 2182 gtag('event', 'Link Click', { 2183 'event_category': analyticsCategory, 2184 'event_label': "CustomerFacebookSocialLinkPlatinum1-2" 2185 }); 2186 }); 2187 $('#yelp-social-footer').on('click', function () { 2188 gtag('event', 'Link Click', { 2189 'event_category': analyticsCategory, 2190 'event_label': "CustomerYelpSocialLinkPlatinum1-2" 2191 }); 2192 }); 2193 $('#twitter-social-footer').on('click', function () { 2194 gtag('event', 'Link Click', { 2195 'event_category': analyticsCategory, 2196 'event_label': "CustomerTwitterSocialLinkPlatinum1-2" 2197 }); 2198 }); 2199 $('#linkedin-social-footer').on('click', function () { 2200 gtag('event', 'Link Click', { 2201 'event_category': analyticsCategory, 2202 'event_label': "CustomerLinkedinSocialLinkPlatinum1-2" 2203 }); 2204 }); 2205 $('#instagram-social-footer').on('click', function () { 2206 gtag('event', 'Link Click', { 2207 'event_category': analyticsCategory, 2208 'event_label': "CustomerInstagramSocialLinkPlatinum1-2" 2209 }); 2210 }); 2211 $('#contactEmailFooter').on('click', function () { 2212 gtag('event', 'Link Click', { 2213 'event_category': analyticsCategory, 2214 'event_label': "CustomerBusinessEmailLinkPlatinum1-2" 2215 }); 2216 }); 2217 $('#btnContactPost').on('click', function () { 2218 gtag('event', 'Link Click', { 2219 'event_category': analyticsCategory, 2220 'event_label': "CustomerContactLeadPostPlatinum1" 2221 }); 2222 }); 2223 $("#external-review-link").on('click', function () { 2224 gtag('event', 'Link Click', { 2225 'event_category': analyticsCategory, 2226 'event_label': "CustomerExternalReviewLinkPlatinum1" 2227 }); 2228 }); 2229 $('#video').on('click', function () { 2230 gtag('event', 'Video Click', { 2231 'event_category': analyticsCategory, 2232 'event_label': "CustomerVideoPlatinum1" 2233 }); 2234 }); 2235 $('#website-social').on('click', function () { 2236 gtag('event', 'Link Click', { 2237 'event_category': analyticsCategory, 2238 'event_label': "CustomerWebsiteSocialLinkPlatinum1-1" 2239 }); 2240 }); 2241 $('#website-social-footer').on('click', function () { 2242 gtag('event', 'Link Click', { 2243 'event_category': analyticsCategory, 2244 'event_label': "CustomerWebsiteSocialLinkPlatinum1-2" 2245 }); 2246 }); 2247 $('#accessibility-social-footer').on('click', function () { 2248 gtag('event', 'Link Click', { 2249 'event_category': analyticsCategory, 2250 'event_label': "CustomerWebsiteAccessibilityLinkPlatinum1" 2251 }); 2252 }); 2253 $('#photos-image-element').on('click', function () { 2254 gtag('event', 'Link Click', { 2255 'event_category': analyticsCategory, 2256 'event_label': "CustomerWebsiteImageryPhotoPlatinum1" 2257 }); 2258 }); 2259 $('#HomePageNavigationLink').on('click', function () { 2260 gtag('event', 'Link Click', { 2261 'event_category': analyticsCategory, 2262 'event_label': "CustomerHomePageNavLinkPlatinum1" 2263 }); 2264 }); 2265 $('#AccessibilityPageNavigationLink').on('click', function () { 2266 gtag('event', 'Link Click', { 2267 'event_category': analyticsCategory, 2268 'event_label': "CustomerAccessibilityPageNavLinkPlatinum1" 2269 }); 2270 }); 2271 $('#ContactPageNavigationLink').on('click', function () { 2272 gtag('event', 'Link Click', { 2273 'event_category': analyticsCategory, 2274 'event_label': "CustomerContactPageNavLinkPlatinum1" 2275 }); 2276 }); 2277 $('#PhotosNavigationLink').on('click', function () { 2278 gtag('event', 'Link Click', { 2279 'event_category': analyticsCategory, 2280 'event_label': "CustomerPhotosPageNavLinkPlatinum1" 2281 }); 2282 }); 2283 $('#testimoinalsPageNavigationLink').on('click', function () { 2284 gtag('event', 'Link Click', { 2285 'event_category': analyticsCategory, 2286 'event_label': "CustomerTestimonialsPageNavLinkPlatinum1" 2287 }); 2288 }); 2289 $('#ServicesPageNavigationLink').on('click', function () { 2290 gtag('event', 'Link Click', { 2291 'event_category': analyticsCategory, 2292 'event_label': "CustomerServicesPageNavLinkPlatinum1" 2293 }); 2294 }); 2295 $('#AboutPageNavigationLink').on('click', function () { 2296 gtag('event', 'Link Click', { 2297 'event_category': analyticsCategory, 2298 'event_label': "CustomerAboutPageNavLinkPlatinum1" 2299 }); 2300 }); 2301 2302 $(document).on('click', '.btn-link', function() { 2303 // Find the closest parent that includes both the button and the items to be toggled 2304 var $parentNav = $(this).closest('.navbar-attribute'); 2305 2306 // Toggle the visibility of hidden items within this specific parent 2307 var $hiddenItems = $parentNav.find('.hidden-item'); 2308 $hiddenItems.toggle(); 2309 2310 // Find the arrow within the clicked button 2311 var $arrow = $(this).find('.arrow'); 2312 2313 // Toggle arrow direction by switching classes 2314 if ($arrow.hasClass('arrow-down')) { 2315 $arrow.removeClass('arrow-down').addClass('arrow-up'); 2316 } else { 2317 $arrow.removeClass('arrow-up').addClass('arrow-down'); 2318 } 2319 }); 2320 2321 $(document).on('click', '#photos-image-element1', function () { 2322 $('#selectedServiceId').val('service1'); 2323 DisplayPage('#servicesPage'); 2324 }); 2325 $(document).on('click', '#photos-image-element2', function () { 2326 $('#selectedServiceId').val('service2'); 2327 DisplayPage('#servicesPage'); 2328 }); 2329 $(document).on('click', '#photos-image-element3', function () { 2330 $('#selectedServiceId').val('service3'); 2331 DisplayPage('#servicesPage'); 2332 }); 2333 2334 //unable to directly track cross domain iFrame clicks. Track only first click on the iFrame itself 2335 var monitor = setInterval(function () { 2336 var elem = document.activeElement; 2337 if (elem && elem.tagName == "IFRAME" && elem.id == "googleMapEmbed") { 2338 gtag('event', 'iFrame Click', { 2339 'event_category': analyticsCategory, 2340 'event_label': "GoogleDirectionsPlatinum1" 2341 }); 2342 clearInterval(monitor); 2343 } 2344 }, 1000); 2345 2346 var navbarHeight = $('#headernav').outerHeight(); // Make sure this matches your navbar class 2347 document.documentElement.style.setProperty('--scroll-padding-top', navbarHeight + 'px'); 2348 });
2349 2350 function scrollToService(serviceId) { 2351 var targetElement = document.getElementById(serviceId); 2352 if (targetElement) { 2353 var headerHeight = document.querySelector('#headernav').offsetHeight; 2354 var descriptionOffset = 165; // Adjust this value as needed 2355 var scrollPosition = targetElement.offsetTop - headerHeight - descriptionOffset; 2356 2357 window.scrollTo({ 2358 top: scrollPosition, 2359 behavior: 'smooth' 2360 }); 2361 } else { 2362 console.log('Service section element not found'); 2363 } 2364 } 2365 2366 function DisplayPage(page, serviceId) { 2367 PauseVideoPlayer(); 2368 2369 if (!$(page).hasClass("show")) { 2370 $("#pages div.collapse.show").collapse("hide"); 2371 } 2372 $(page).collapse("show"); 2373 2374 if(page == "#homePage") 2375 { 2376 var carousel = $(".slick-slider"); 2377 if(carousel != null) 2378 { 2379 carousel.slick('refresh'); 2380 } 2381 } 2382 2383 if (page == "#testimonialsPage") { 2384 // Load Testimonials via Ajax if not already loaded 2385 if (!testimonialsRendered) { 2386 ShowLoading(); 2387 $.get('/Home/GetTestimonialsContent', { id: 16169, template: "Platinum_1"}, function (data) { 2388 if (data != "") 2389 { 2390 $("#testimonialsPage").append(data); 2391 } 2392 }).always(function() { 2393 HideLoading(); 2394 testimonialsRendered = true; 2395 }); 2396 } 2397 2398 $("html, body").animate({ scrollTop: 0 }, "fast"); 2399 } 2400 2401 if (page == "#aboutPage") { 2402 // Load About via Ajax if not already loaded 2403 if (!aboutRendered) { 2404 ShowLoading(); 2405 $.get('/Home/GetAboutContent', { id: 16169, template: "Platinum_1"}, function (data) { 2406 if (data != "") 2407 { 2408 $("#aboutPage").append(data); 2409 $.getScript("https://maps.googleapis.com/maps/api/js?key=AIzaSyB2UoPWVWPR8WrIFK_zqJ5nTN1NvGm9dqY&callback=initMap"); 2410 2411 } 2412 }).always(function() { 2413 HideLoading(); 2414 aboutRendered = true; 2415 }); 2416 } 2417 } 2418 2419 if (page === "#servicesPage") 2420 { 2421 // Load Services via Ajax if not already loaded 2422 if (!servicesRendered) { 2423 ShowLoading(); 2424 $.get('/Home/GetServicesContent', { id: 16169, template: "Platinum_1" }) 2425 .done(function (data) { 2426 if (data !== "") { 2427 $("#servicesPage").append(data); 2428 servicesRendered = true; 2429 // Trigger the scroll after a short delay 2430 setTimeout(function() { 2431 var selectedServiceId = $('#selectedServiceId').val(); 2432 if (selectedServiceId) { 2433 scrollToService(selectedServiceId); 2434 $('#selectedServiceId').val(''); 2435 } 2436 }, 300); // Adjust the delay as needed 2437 } 2438 }) 2439 .always(function () { 2440 HideLoading(); 2441 }); 2442 } else { 2443 // If the services are already rendered, trigger the scroll after a short delay 2444 setTimeout(function() { 2445 var selectedServiceId = $('#selectedServiceId').val(); 2446 if (selectedServiceId) { 2447 scrollToService(selectedServiceId); 2448 $('#selectedServiceId').val(''); 2449 } 2450 }, 100); // Adjust the delay as needed 2451 } 2452 } else { 2453 window.scrollTo(0, 0); 2454 } 2455 2456 if (page == "#imageryPage") { 2457 // Load photos via Ajax if not already loaded 2458 if (!photosRendered) { 2459 ShowLoading(); 2460 $.get('/Home/GetPhotosContent', { id: 16169, template: "Platinum_1"}, function (data) { 2461 if (data != "") 2462 { 2463 $("#imageryPage").append(data); 2464 } 2465 }).always(function() { 2466 HideLoading(); 2467 photosRendered = true; 2468 }); 2469 } 2470 } 2471 2472 if (page == "#contactPage") { 2473 // Load Contact via Ajax if not already loaded 2474 if (!contactRendered) { 2475 ShowLoading(); 2476 $.get('/Home/GetContactContent', { id: 16169, template: "Platinum_1"}, function (data) { 2477 if (data != "") 2478 { 2479 $("#contactPage").append(data); 2480 } 2481 }).always(function() { 2482 HideLoading(); 2483 contactRendered = true; 2484 }); 2485 } 2486 } 2487 2488 if (page == "#locationsPage") { 2489 // Load Locations via Ajax if not already loaded 2490 if (!locationsRendered) { 2491 ShowLoading(); 2492 2493 $.get('/Home/GetLocationsContent', { id: 16169, template: "Platinum_1" }, function (data) { 2494 if (data != "") 2495 { 2496 $("#locationsPage").append(data); 2497 } 2498 }).always(function() { 2499 HideLoading(); 2500 locationsRendered = true; 2501 }); 2502 } 2503 } 2504 2505 if (page == "#faqPage") { 2506 // Load Faqs via Ajax if not already loaded 2507 if (!faqRendered) { 2508 ShowLoading(); 2509 2510 $.get('/Home/GetFAQContent', { id: 16169, template: "Platinum_1" }, function (data) { 2511 if (data != "") { 2512 $("#faqPage").append(data); 2513 } 2514 }).always(function () { 2515 HideLoading(); 2516 faqRendered = true; 2517 }); 2518 } 2519 } 2520 2521 if (page == "#contentPage") { 2522 // Load Additional Content via Ajax if not already loaded 2523 if (!additionalContentRendered) { 2524 ShowLoading(); 2525 2526 $.get('/Home/GetAdditionalContentContent', { id: 16169, template: "Platinum_1" }, function (data) { 2527 if (data != "") { 2528 $("#contentPage").append(data); 2529 } 2530 }).always(function () { 2531 HideLoading(); 2532 additionalContentRendered = true; 2533 }); 2534 } 2535 } 2536 } 2537 2538 function PauseVideoPlayer() { 2539 2540 var videos = document.querySelectorAll('video'); 2541 var iframes = document.querySelectorAll('iframe'); 2542 2543 //mp4 2544 Array.prototype.forEach.call(videos, video => { 2545 video.pause(); 2546 }); 2547 2548 //youtube 2549 Array.prototype.forEach.call(iframes, iframe => { 2550 iframe.contentWindow.postMessage(JSON.stringify({ 2551 event: 'command', 2552 func: 'pauseVideo' 2553 }), '*'); 2554 }); 2555 2556 //wistia + vimeo 2557 Array.prototype.forEach.call(iframes, iframe => { 2558 iframe.contentWindow.postMessage(JSON.stringify({ 2559 "method": "pause", 2560 "value": "true" 2561 }), '*'); 2562 }); 2563 } 2564 2565 function GetPostsData(i) 2566 { 2567 if(i != 0){ 2568 ShowLoading(); 2569 } 2570 postsPageIndex = postsPageIndex + i; 2571 $.get('/Home/GetTestimonialPostsInfo', {id: 16169, program_id: 18, profileDomain: 'https://rocksolidforminginc.com/', subdomain: 'rocksolidforminginc', testimonialSSL : 'False', domainSSL : 'True', pageIndex: postsPageIndex, pageSize: pageSize, file: '_TestimonialPostsContent'}, function (data) { 2572 if (data != "") 2573 { 2574 $(".postsInfo").html(data); 2575 } 2576 }).always(function() { 2577 HideLoading(); 2578 }); 2579 SetPostsPaginationButtons(); 2580 } 2581 2582 function GetPostsBanner() 2583 { 2584 ShowLoading(); 2585 var bannerObj = {"id":450148,"urgency_item_type_id":16,"urgency_status_id":4,"delay_reason":null,"delay_notification_sent":null,"created":"2026-07-13T12:01:07","created_by":"5ea4aaf8-4319-11f1-9961-12103b750b51","created_note":"AMPLibrary.Optima2.Urgency.UrgencyItem.AddUrgencyItem()","modified":"2026-07-14T02:13:08","modified_by":"Personiv User5","modified_note":"AMPLibrary.Optima2.Urgency.UrgencyItem.UpdateUrgencyItem()","deleted":null,"deleted_by":null,"deleted_note":null,"profile_review_id":null,"review_response":null,"profile_review_response_image_id":null,"google_post_title":"Thinking about your next build? ð¤","google_post_start":"2026-07-13T12:00:00","google_post_end":"2027-07-01T12:00:00","google_post_body":"Donât leave your foundation to chanceâtrust the pros at Rock Solid Forming Inc to get the job done right from the ground up. \r\nâï¸ Reliable team \r\nâï¸ Quality work \r\nâï¸ Built to last \r\nð² Message or call us today for a quote!","google_post_network_path":"\\\\w19-amp-imagery\\optima_media\\googlepost\\P_16169\\838c0e49-2979-4154-8bad-f4e549e0d123.jpeg","google_post_web_path":"https://du9m0k402rjmo.
2585cloudfront.net/googlepost/P_16169\\838c0e49-2979-4154-8bad-f4e549e0d123.jpeg","google_post_filename":"838c0e49-2979-4154-8bad-f4e549e0d123.jpeg","google_post_option":"None","google_post_link":"Thinking about your next build? ??","completed_notification_sent":null,"completed_feedback":null,"post_display_date":"2026-07-13T00:00:00","post_to_google":true,"post_to_testimonial":true,"attention_grabber":"ROCK SOLID\r\nSTART HERE","deleted_reason":null,"urgency_google_post_option_id":7,"post_feature":true,"post_feature_expiration":"2027-07-01T12:00:00","crm_post_id":124211,"ignore_end_dates":false,"hide_start_date":true,"hide_expiration_date":true,"google_post_submitted":false,"keep_feature_post_up":false,"api_status":null,"api_message":null,"api_posted":null}; 2586 $.ajax({ 2587 method: "POST", 2588 url: "/Home/GetPostBanner", 2589 data:JSON.stringify( 2590 { 2591 banner: bannerObj, 2592 testimonialPageColor: (false) ? 'transparent' :'', 2593 postsColor: '#ffffff' 2594 }), 2595 contentType: 'application/json', 2596 success: function (data) { 2597 var holdaspect = ""; 2598 if ($("#homepagebanner").hasClass('aspect-ratio-16x9')) { 2599 $('#homepagebanner').removeClass('aspect-ratio-16x9'); 2600 holdaspect = "aspect-ratio-16x9"; 2601 } 2602 2603 if(isMobile || isTablet){ 2604 setTimeout(function(){ 2605 $(".home-content").prepend(data); 2606 }, 5000); 2607 } 2608 else{ 2609 $(".home-content").prepend(data); 2610 } 2611 2612 $('#bannerclose').click(function() { 2613 if (holdaspect != "") { 2614 $('#homepagebanner').addClass('aspect-ratio-16x9'); 2615 } 2616 }); 2617 2618 /*remove banner after 30 seconds regardless of mobile/tablet/desktop*/ 2619 if (!keepPostUp) { 2620 setTimeout(function () { 2621 $("#posts-banner-alert").remove(); 2622 if (holdaspect != "" && !(isMobile || isTablet)) { 2623 $('#homepagebanner').addClass('aspect-ratio-16x9'); 2624 } 2625 }, 30000); 2626 } 2627 } 2628 }).always(function() { 2629 HideLoading(); 2630 if (holdaspect != "" && !(isMobile || isTablet)) { 2631 $('#homepagebanner').addClass('aspect-ratio-16x9'); 2632 } 2633 }); 2634 } 2635 2636 function SetPostsPaginationButtons() 2637 { 2638 //look ahead by one 2639 if (((postsPageIndex + 1) * pageSize) >= numOfPosts) { 2640 $("#nextPosts").hide(); 2641 } 2642 else { 2643 $("#nextPosts").show(); 2644 $("#nextPosts").prop('disabled', false); 2645 } 2646 2647 if (postsPageIndex <= 0) { 2648 postsPageIndex = 0; 2649 $("#prevPosts").hide(); 2650 } 2651 else if (postsPageIndex > 0) { 2652 $("#prevPosts").show(); 2653 $("#prevPosts").prop('disabled', false); 2654 } 2655 } 2656 2657 function ShowLoading() 2658 { 2659 $("#loadingImg").show(); 2660 } 2661 2662 function HideLoading() 2663 { 2664 $("#loadingImg").hide(); 2665 } 2666 2667 function checkMobile() { 2668 var windowsize = $(window).width(); 2669 switch (true) { 2670 case /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent): 2671 isMobile = true; 2672 break; 2673 case windowsize < 800: 2674 isMobile = true; 2675 break; 2676 default: 2677 isMobile = false; 2678 } 2679 } 2680 2681 function checkTablet() { 2682 var windowsize = $(window).width(); 2683 switch (true) { 2684 case (windowsize >= 800) && (windowsize < 1200): 2685 isTablet = true; 2686 break; 2687 default: 2688 isTablet = false; 2689 } 2690 } 2691 2692 function checkHashRoute() { 2693 if ((location.hash != null) && (location.hash != "") && (location.hash == '#homePage' || location.hash == '#aboutPage' || location.hash == '#servicesPage' || location.hash == '#testimonialsPage' || location.hash == '#contactPage' || location.hash == '#locationsPage' || location.hash == '#faqPage' || location.hash == '#contentPage')) { 2694 DisplayPage(location.hash); 2695 } 2696 } 2697 2698 function contactUsPost(event) { 2699 var _contactForm = $("#ContactPostForm"); 2700 2701 // Clear previous validation states 2702 $('.validation-error-message').remove(); 2703 _contactForm.find('.validation-error').removeClass('validation-error'); 2704 $("#contactResponseMessage").empty(); 2705 2706 if (_contactForm[0].checkValidity()) { 2707 contactUsSubmit(event); 2708 } else { 2709 var firstInvalidField = null; 2710 2711 _contactForm.find('input[required], textarea[required], select[required]').each(function () { 2712 var $field = $(this); 2713 2714 if (!this.checkValidity()) { 2715 var fieldId = $field.attr('id') || $field.attr('name'); 2716 var fieldLabel = getFieldLabel(fieldId); 2717 2718 $field.addClass('validation-error'); 2719 2720 var errorMessage = getCustomErrorMessage($field, fieldLabel); 2721 $field.after(`<div class="validation-error-message">${errorMessage}</div>`); 2722 2723 if (!firstInvalidField) { 2724 firstInvalidField = $field; 2725 } 2726 } 2727 }); 2728 2729 // Focus first invalid field 2730 if (firstInvalidField) { 2731 firstInvalidField.focus(); 2732 } 2733 2734 // Re-enable submit button 2735 $("#btnContactPost").prop('disabled', false); 2736 $("#btnContactPost").css('font-weight', 'bold'); 2737 } 2738 } 2739 2740 function getFieldLabel(fieldId) { 2741 var labels = { 2742 'contact_us_phone': 'Phone Number', 2743 'contact_us_address': 'Address', 2744 'contact_us_city': 'City', 2745 'contact_us_state': 'State', 2746 'contact_us_zip': 'ZIP Code', 2747 'contact_us_subject': 'Subject', 2748 'contact_us_name': 'Name', 2749 'contact_us_email': 'Email', 2750 'contact_us_message': 'Message' 2751 }; 2752 return labels[fieldId] || 'This field'; 2753 } 2754 2755 function getCustomErrorMessage($field, fieldLabel) { 2756 if ($field.prop('required') && !$field.val().trim()) { 2757 return `${fieldLabel} required`; 2758 } 2759 2760 if ($field.attr('type') === 'email' && $field.val()) { 2761 return `Please enter a valid email address.`; 2762 } 2763 2764 return `Please enter a valid ${fieldLabel.toLowerCase()}.`; 2765 } 2766 2767 function contactUsSubmit(event) { 2768 event.preventDefault(); 2769 2770 var form = $("#ContactPostForm"); 2771 grecaptcha.ready(function() { 2772 grecaptcha.execute('6LcokacUAAAAAH_snk3O5bpdYwjpC5XAuXNLrJLH', {action: 'contact_us'}).then(function(token) { 2773 $("#g-Recaptcha-Response-Contact").attr("value", token); //add token for server side validation 2774 $.ajax({
2775 type: "POST", 2776 url: "/Home/ContactPost", 2777 data: form.serialize(), 2778 success: function(data){ 2779 if (data.indexOf('{"message":') != -1) { 2780 var jsonData = JSON.parse(data); 2781 if (!jsonData.success) { 2782 $("#contactResponseMessage").html('<div class="alert alert-warning alert-dissmisable" role="alert"><button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' + jsonData.message + '</div>'); 2783 recaptchaContactUsInComplete(); 2784 showClassicCaptcha("contact"); 2785 } 2786 else{ 2787 $("#contactResponseMessage").html('<div class="alert alert-success alert-dissmisable" role="alert"><button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' + jsonData.message + '</div>'); 2788 recaptchaContactUsComplete(); 2789 } 2790 } 2791 } 2792 }); 2793 }); 2794 }); 2795 } 2796 2797 function ScrollForPosts(e){ 2798 // target element 2799 var $id = $(".posts-partial-content"); 2800 if ($id.length === 0) { 2801 return; 2802 } 2803 2804 // prevent standard hash navigation (avoid blinking in IE) 2805 e.preventDefault(); 2806 2807 // top position relative to the document 2808 var pos = $id.offset().top - $('.navbar').height(); 2809 2810 // animated top scrolling 2811 // .stop() keeps from scrolling to top then to element 2812 $('body, html').stop().animate({ scrollTop: pos }, 500); 2813 } 2814 2815 function recaptchaContactUsInComplete(){ 2816 $("#btnContactPost").prop('disabled', false); 2817 $("#btnContactPost").css('font-weight', 'bold'); 2818 } 2819 2820 function recaptchaContactUsComplete() { 2821 $("#btnContactPost").prop('disabled', false); 2822 $("#btnContactPost").css('font-weight', 'normal'); 2823 $("#ContactPostForm").trigger("reset"); 2824 } 2825 2826 function recaptchaReviewIncomplete(){ 2827 $("#postReview").prop('disabled', false); 2828 $("#postReview").css('font-weight', 'bold'); 2829 } 2830 2831 function recaptchaReviewComplete(){ 2832 $("#postReview").css('font-weight', 'normal'); 2833 $("#postReviewForm").trigger("reset"); 2834 $("#postReview").prop('disabled', false); 2835 } 2836 2837 function ClearReviewFields() { 2838 $('#starRating').barrating('clear'); 2839 $("#review").val(""); 2840 $("#review_alias").val(""); 2841 $("#review_heading").val(""); 2842 $("#reviewCaptcha").val(""); 2843 } 2844 2845 function resetReviewButton($button) { 2846 var $buttonText = $button.find('.button-text'); 2847 var $spinner = $button.find('.spinner'); 2848 2849 $button.prop('disabled', false); 2850 2851 if ($buttonText.length && $spinner.length) { 2852 $buttonText.show(); 2853 $spinner.hide(); 2854 } else { 2855 var originalText = $button.data('original-text'); 2856 if (originalText) { 2857 $button.html(originalText); 2858 } 2859 } 2860 } 2861 2862 function setProcessingState($button, $form, isProcessing) { 2863 var $buttonText = $button.find('.button-text'); 2864 var $spinner = $button.find('.spinner'); 2865 2866 if (isProcessing) { 2867 $button.prop('disabled', true); 2868 $form.addClass('review-form-processing'); 2869 $form.find('input, textarea, select').not('#profile_id, #g-Recaptcha-Response-Review, [type="hidden"]').prop('disabled', true); 2870 2871 if ($buttonText.length && $spinner.length) { 2872 $buttonText.hide(); 2873 $spinner.show(); 2874 } else { 2875 $button.data('original-text', $button.html()); 2876 $button.html('<i class="fas fa-spinner fa-spin"></i> Processing...'); 2877 } 2878 } else { 2879 $button.prop('disabled', false); 2880 $form.removeClass('review-form-processing'); 2881 $form.find('input, textarea, select').prop('disabled', false); 2882 2883 if ($buttonText.length && $spinner.length) { 2884 $buttonText.show(); 2885 $spinner.hide(); 2886 } else { 2887 var originalText = $button.data('original-text'); 2888 if (originalText) { 2889 $button.html(originalText); 2890 } 2891 } 2892 } 2893 } 2894 2895 function ClearReviewFields() { 2896 console.log('Clearing review fields'); 2897 $('#starRating').barrating('clear'); 2898 $("#review").val(""); 2899 $("#review_alias").val(""); 2900 $("#review_heading").val(""); 2901 $("#reviewCaptcha").val(""); 2902 2903 $("#postReviewForm input, #postReviewForm textarea").removeClass('error'); 2904 $("#postReviewForm .br-widget").removeClass('error'); 2905 } 2906 2907 function validateReview(btn) { 2908 var $form = $("#postReviewForm"); 2909 var isValid = true; 2910 2911 console.log('Starting validation...'); 2912 2913 $form.find('input, textarea').removeClass('error'); 2914 $form.find('.br-widget').removeClass('error'); 2915 2916 var reviewText = $('#review').val(); 2917 console.log('Review text length:', reviewText ? reviewText.trim().length : 0); 2918 2919 if (!reviewText || reviewText.trim().length === 0) { 2920 console.log('Review text validation failed - empty or whitespace only'); 2921 $('#review').addClass('error'); 2922 $('#review').focus(); 2923 isValid = false; 2924 } else { 2925 console.log('Review text validation passed'); 2926 } 2927 2928 var rating = null; 2929 var selectRating = $('#starRating')[0] ? $('#starRating')[0].value : null; 2930 var jqueryRating = $('#starRating').val(); 2931 var selectedOption = $('#starRating option:selected').val(); 2932 var barRating = null; 2933 2934 if (typeof $('#starRating').barrating === 'function') { 2935 try { 2936 var barResult = $('#starRating').barrating('show'); 2937 if (barResult && typeof barResult === 'string') { 2938 barRating = barResult; 2939 } else if (barResult && typeof barResult === 'number') { 2940 barRating = barResult.toString(); 2941 } 2942 } catch (e) { 2943 console.log('Error getting barrating value:', e); 2944 } 2945 } 2946 2947 if (jqueryRating && typeof jqueryRating === 'string') { 2948 } else if (jqueryRating && typeof jqueryRating === 'number') { 2949 jqueryRating = jqueryRating.toString(); 2950 } else { 2951 jqueryRating = null; 2952 } 2953 2954 if (barRating && barRating !== "" && barRating !== "0") { 2955 rating = barRating; 2956 } else if (selectRating && selectRating !== "" && selectRating !== "0") { 2957 rating = selectRating; 2958 } else if (selectedOption && selectedOption !== "" && selectedOption !== "0") { 2959 rating = selectedOption; 2960 } else if (jqueryRating && jqueryRating !== "" && jqueryRating !== "0") { 2961 rating = jqueryRating; 2962 } 2963 2964 if (rating && typeof rating !== 'string') { 2965 console.log('WARNING: Rating is not a string, converting:', rating, typeof rating);
2966 rating = String(rating); 2967 } 2968 2969 if (rating && typeof rating === 'object') { 2970 console.error('CRITICAL ERROR: Rating is still an object!', rating); 2971 rating = null; 2972 } 2973 2974 console.log('Validation - Final rating value:', rating, typeof rating); 2975 2976 if (!rating || rating === "" || rating === "0") { 2977 console.log('Rating validation failed'); 2978 $('.br-widget').addClass('error'); 2979 isValid = false; 2980 } else { 2981 console.log('Rating validation passed with value:', rating); 2982 } 2983 2984 console.log('Validation result:', isValid); 2985 return isValid; 2986 } 2987 2988 function validateReviewLength() { 2989 var text = $('#review').val(); 2990 var success = true; 2991 2992 if (text.trim().length == 0) { 2993 $('#review').addClass('error'); 2994 success = false; 2995 } 2996 return success; 2997 } 2998 2999 function validateRating() { 3000 var rating = null; 3001 3002 // Use the same rating detection logic 3003 var selectRating = $('#starRating')[0] ? $('#starRating')[0].value : null; 3004 var jqueryRating = $('#starRating').val(); 3005 var selectedOption = $('#starRating option:selected').val(); 3006 var barRating = null; 3007 3008 if (typeof $('#starRating').barrating === 'function') { 3009 try { 3010 barRating = $('#starRating').barrating('show'); 3011 } catch (e) { 3012 console.log('Error getting barrating value in validateRating:', e); 3013 } 3014 } 3015 3016 // Determine which rating to use 3017 if (barRating && barRating !== "" && barRating !== "0") { 3018 rating = barRating; 3019 } else if (selectRating && selectRating !== "" && selectRating !== "0") { 3020 rating = selectRating; 3021 } else if (selectedOption && selectedOption !== "" && selectedOption !== "0") { 3022 rating = selectedOption; 3023 } else if (typeof jqueryRating === 'string' && jqueryRating !== "" && jqueryRating !== "0") { 3024 rating = jqueryRating; 3025 } 3026 3027 var success = true; 3028 if (!rating || rating === "" || rating === "0") { 3029 $('.br-widget').addClass('error'); 3030 success = false; 3031 } 3032 return success; 3033 } 3034</script>
3034 3035</body> 3036</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.