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